diff --git a/README.md b/README.md index 7e15eda3d0..f6d3f32ba2 100644 --- a/README.md +++ b/README.md @@ -22,15 +22,13 @@ Turns any MySQL, PostgreSQL, SQL Server, SQLite & MariaDB into a smart-spreadshe Twitter

-

- -

-

+![OpenSourceAirtableAlternative](https://user-images.githubusercontent.com/5435402/133762127-e94da292-a1c3-4458-b09a-02cd5b57be53.png) NocoDB - The Open Source Airtable alternative | Product Hunt + # Quick try ### 1-Click Deploy @@ -78,29 +76,43 @@ Access Dashboard using : [http://localhost:8080/dashboard](http://localhost:8080
# Screenshots - -
- + +![2](https://user-images.githubusercontent.com/5435402/133759229-4275b934-873b-4a9b-9f23-96470fec9775.png)
- + +![1](https://user-images.githubusercontent.com/5435402/133759218-f8b0bffc-707f-451c-82f2-b5ba2573d6a6.png)
- + +![7](https://user-images.githubusercontent.com/5435402/133759245-a536165b-55f1-46a8-a74e-1964e7e481c6.png)
- + +![5](https://user-images.githubusercontent.com/5435402/133759240-dd3f2509-aab7-4bd1-9a58-4c2dff08f2f2.png)
- + +![6](https://user-images.githubusercontent.com/5435402/133759242-2311a127-17c8-406c-b865-1a2e9c8ee398.png)
- + +![3](https://user-images.githubusercontent.com/5435402/133759234-9b539029-be36-4a35-b55a-bee3ebd0e010.png)
- + +![4](https://user-images.githubusercontent.com/5435402/133759236-dc182129-3768-4e23-874e-98f5f32e962c.png)
- + +![11](https://user-images.githubusercontent.com/5435402/133759253-bb9bc729-ee28-4f86-ab95-7d112c0815f2.png)
- + +![10](https://user-images.githubusercontent.com/5435402/133759250-ebd75ecf-31db-4a17-b2d7-2c43af78a54e.png)
- + +![8](https://user-images.githubusercontent.com/5435402/133759248-3a7141e0-4b7d-4079-a5f9-cf8611d00bc5.png)
+![9](https://user-images.githubusercontent.com/5435402/133759249-8c1a85c2-a55c-48f6-bd58-aa6b4195cce7.png) + + + + + @@ -291,3 +303,6 @@ Our mission is to provide the most powerful no-code interface for databases whic + + + diff --git a/cypress.json b/cypress.json index a2fe63de9b..245acfe53c 100644 --- a/cypress.json +++ b/cypress.json @@ -1,10 +1,11 @@ { - "baseUrl": "http://localhost:3000", + "baseUrl": "http://localhost:3000/", "defaultCommandTimeout": 13000, "pageLoadTimeout": 600000, "viewportWidth": 1980, "viewportHeight": 1000, "video": false, "retries": 2, - "screenshotOnRunFailure": false + "screenshotOnRunFailure": false, + "numTestsKeptInMemory": 5 } diff --git a/cypress/integration/common/_pre_configurations.js b/cypress/integration/common/_pre_configurations.js new file mode 100644 index 0000000000..b7f00210cb --- /dev/null +++ b/cypress/integration/common/_pre_configurations.js @@ -0,0 +1,93 @@ + +// Cypress test suite: project pre-configurations +// + +import { loginPage, projectsPage } from "../../support/page_objects/navigation" +import { mainPage } from "../../support/page_objects/mainpage" +import { staticProjects, roles } from "../../support/page_objects/projectConstants" + +describe(`Project pre-configurations`, () => { + + it('Admin SignUp', ()=> { + cy.waitForSpinners(); + cy.signinOrSignup(roles.owner.credentials) + cy.wait(2000) + }) + + const createProject = (proj) => { + it(`Create ${proj.basic.name} project`, () => { + + // click home button + mainPage.toolBarTopLeft(mainPage.HOME).click() + // create requested project + projectsPage.createProject( proj.basic, proj.config ) + }) + } + + createProject(staticProjects.sampleREST) + createProject(staticProjects.sampleGQL) + createProject(staticProjects.externalREST) + createProject(staticProjects.externalGQL) +}) + +describe('Static user creations (different roles)', () => { + + beforeEach(()=> { + loginPage.signIn(roles.owner.credentials) + projectsPage.openProject(staticProjects.sampleREST.basic.name) + }) + + const addUser = (user) => { + it(`RoleType: ${user.name}`, () => { + mainPage.addNewUserToProject(user.credentials, user.name) + }) + } + + addUser(roles.creator) + addUser(roles.editor) + addUser(roles.commenter) + addUser(roles.viewer) +}) + +describe('Static users- add to other static projects', () => { + + const addUserToProject = (proj) => { + it(`Add users to ${proj.basic.name}`, () => { + loginPage.signIn(roles.owner.credentials) + projectsPage.openProject(proj.basic.name) + + mainPage.addExistingUserToProject(roles.creator.credentials.username, roles.creator.name) + mainPage.addExistingUserToProject(roles.editor.credentials.username, roles.editor.name) + mainPage.addExistingUserToProject(roles.commenter.credentials.username, roles.commenter.name) + mainPage.addExistingUserToProject(roles.viewer.credentials.username, roles.viewer.name) + }) + } + + addUserToProject(staticProjects.sampleGQL) + addUserToProject(staticProjects.externalREST) + addUserToProject(staticProjects.externalGQL) +}) + + + +/** + * @copyright Copyright (c) 2021, Xgene Cloud Ltd + * + * @author Raju Udava + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ \ No newline at end of file diff --git a/cypress/integration/common/share_view.js b/cypress/integration/common/share_view.js new file mode 100644 index 0000000000..4981d7f4a7 --- /dev/null +++ b/cypress/integration/common/share_view.js @@ -0,0 +1,157 @@ +import { loginPage, projectsPage } from "../../support/page_objects/navigation" + +const shareViewWithPwd = (pwdCorrect, pwd) => { + + cy.get('.v-navigation-drawer__content > .container') + .find('.v-list > .v-list-item') + .contains('Share View') + .click() + + // No password- unprotected link + if(''==pwd) { + + // copy link text, visit URL + cy.getActiveModal().find('.share-link-box') + .contains('http', {timeout: 2000}) + .then(($obj) => { + + let linkText = $obj.text() + cy.log(linkText) + cy.visit(linkText) + + // wait for share view page to load! + cy.wait(5000) + cy.get('body').find('.v-dialog.v-dialog--active').should('not.exist') + }) + + // password protected link + } else { + + // enable checkbox & feed pwd, save + cy.getActiveModal().find('[role="switch"][type="checkbox"]').click( {force: true} ) + cy.getActiveModal().find('input[type="password"]').type('123456') + cy.getActiveModal().find('button:contains("Save password")').click() + + // copy link text, visit URL + cy.getActiveModal().find('.share-link-box') + .then(($obj) => { + + let linkText = $obj.text() + cy.log(linkText) + cy.visit(linkText) + + // wait for share view page to load! + cy.wait(5000) + + // feed password + cy.getActiveModal().find('input[type="password"]').type(pwd) + cy.getActiveModal().find('button:contains("Unlock")').click() + + cy.wait(1000) + + // if pwd is incorrect, active modal requesting to feed in password again + // will remain + if(pwdCorrect) { + cy.get('body').find('.v-dialog.v-dialog--active').should('not.exist') + } + else { + cy.get('body').find('.v-dialog.v-dialog--active').should('exist') + } + }) + } +} + + +// delete created views +// +const deleteCreatedViews = () => { + cy.get('.v-navigation-drawer__content > .container') + .find('.v-list > .v-list-item') + .contains('Share View') + .parent().find('button.mdi-dots-vertical').click() + + cy.getActiveMenu().find('.v-list-item').contains('Views List').click() + + cy.get(1000) + + cy.get('th:contains("View Link")').parent().parent() + .next().find('tr').each((tableRow) => { + cy.wrap(tableRow).find('button').last().click() + cy.wait(1000) + }) +} + +const genTest = (type) => { + + describe(`${type.toUpperCase()} api - Clipboard access`, () => { + + let projectName + + // create project with default credentials to work with + // + before( () => { + + loginPage.signIn({ username: 'user@nocodb.com', password: 'Password123.' }) + + if(type == 'rest') + projectName = projectsPage.createDefaulRestProject() + else + projectName = projectsPage.createDefaultGraphQlProject() + }) + + // Run once before test- create project (rest/graphql) + // + beforeEach(() => { + loginPage.signIn({ username: 'user@nocodb.com', password: 'Password123.' }) + projectsPage.openProject(projectName) + + cy.openTableTab('City'); + }) + + it('Share view without password', () => { + shareViewWithPwd(true, '') + }) + + it('Share view with correct password', () => { + shareViewWithPwd(true, '123456') + }) + + it('Share view with incorrect password', () => { + shareViewWithPwd(false, 'abc') + }) + + it('Delete view', deleteCreatedViews ) + + // clean up + after( () => { + loginPage.signIn({ username: 'user@nocodb.com', password: 'Password123.' }) + projectsPage.deleteProject(projectName) + }) + }) +} + +genTest('rest') +genTest('graphql') + +/** + * @copyright Copyright (c) 2021, Xgene Cloud Ltd + * + * @author Pranav C Balan + * @author Raju Udava + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ \ No newline at end of file diff --git a/cypress/integration/ncTest.spec.js b/cypress/integration/ncTest.spec.js index 26a26ad32b..416f22932e 100644 --- a/cypress/integration/ncTest.spec.js +++ b/cypress/integration/ncTest.spec.js @@ -1,10 +1,3 @@ -/** - * file: ncTest.Spec.js - * purpose: test-suite-1 - * author: raju udava - * date: 06 Sep 2020 - * - **/ import { loginPage, projectsPage } from "../support/page_objects/navigation" @@ -45,9 +38,53 @@ describe('Login & project page', () => { projectsPage.deleteProject(projectName) }) + it('3. Create project: NC_DB_EXTERNAL, NC_REST', () => { + const projectParams = { dbType: 1, apiType: 1, name: 'externalREST' } + const databaseParams = { + databaseType: 0, + hostAddress: 'localhost', + portNumber: '3306', + username: 'root', + password: 'password', + databaseName: 'sakila' + } + + projectName = projectsPage.createProject(projectParams, databaseParams) + + }) + + it('3a. Delete project: NC_DB_EXTERNAL, NC_REST', () => { + projectsPage.deleteProject(projectName) + }) + + + // Needs to be conditionally triggered if required // // it('N. Clean up: Delete call projects', () => { // projectsPage.deleteAllProject() // }) }) + + +/** + * @copyright Copyright (c) 2021, Xgene Cloud Ltd + * + * @author Raju Udava + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ \ No newline at end of file diff --git a/cypress/integration/user_role_spec.js b/cypress/integration/user_role_spec.js index 44273bb675..e024c0e365 100644 --- a/cypress/integration/user_role_spec.js +++ b/cypress/integration/user_role_spec.js @@ -38,63 +38,6 @@ const roles = { } -// add new user to specified role -// -const addUser = (userCred, role) => { - - let linkText - - // click on New User button, feed details - cy.get('button:contains("New User")').first().click() - cy.get('label:contains(Email)').next('input').type(userCred.username).trigger('input') - cy.get('label:contains(Select User roles)').click() - - // opt-in requested role & submit - // note that, 'editor' is set by default - // - cy.getActiveMenu().contains(role).click() - cy.getActiveMenu().contains('editor').click() - cy.get('.mdi-menu-down').click() - cy.get('.nc-invite-or-save-btn').click() - - // get URL, invoke - cy.getActiveModal().find('.v-alert').then(($obj) => { - linkText = $obj.text() - cy.log(linkText) - - cy.visit(linkText) - }) - - // SignUp is taken care under userRoleCreation() - // wait for some time to ensure screen is loaded - // - cy.wait(2000) -} - - -const userRoleCreation = (roleType) => { - it(`User creation: ${roleType}`, () => { - - loginPage.signIn(roles.owner.credentials) - projectsPage.openProject('sakilaDb') - - // Team & Auth tab is open by default - // - addUser(roles[roleType].credentials, roleType) - - // Redirected to new URL, feed details - // - cy.get('input[type="text"]').type(roles[roleType].credentials.username) - cy.get('input[type="password"]').type(roles[roleType].credentials.password) - cy.get('button:contains("SIGN UP")').click() - - cy.url({ timeout: 6000 }).should('contain', '#/project') - cy.wait(1000) - - projectsPage.openProject('sakilaDb') - }) -} - describe('Test project creation, user creation', () => { let projectName = '' diff --git a/cypress/support/commands.js b/cypress/support/commands.js index 9eae51efa1..96814986a8 100644 --- a/cypress/support/commands.js +++ b/cypress/support/commands.js @@ -37,7 +37,10 @@ Cypress.Commands.add('waitForSpinners', () => { }) cy.get('#nuxt-loading', {timeout: 10_0000}).should('have.length', 0) }) -Cypress.Commands.add('signinOrSignup', () => { + + +Cypress.Commands.add('signinOrSignup', (_args) => { + const args = Object.assign({username: 'user@nocodb.com', password: 'Password123.'}, _args) // signin/signup cy.get('body').then(($body) => { @@ -49,21 +52,21 @@ Cypress.Commands.add('signinOrSignup', () => { cy.wait(8000); cy.get('body').trigger('mousemove'); cy.contains('Let\'s Begin').click(); - cy.get('input[type="text"]', {timeout: 12000}).type('user@nocodb.com'); - cy.get('input[type="password"]').type('Password123.'); + cy.get('input[type="text"]', { timeout: 12000 }).type(args.username); + cy.get('input[type="password"]').type(args.password); cy.get('button:contains("SIGN UP")').click() // handle signin } else { - cy.get('input[type="text"]').type('user@nocodb.com'); - cy.get('input[type="password"]').type('Password123.'); + cy.get('input[type="text"]', { timeout: 12000 }).type(args.username); + cy.get('input[type="password"]').type(args.password); cy.get('button:contains("SIGN IN")').click() } } - }) }) }); + // for opening/creating a rest project Cypress.Commands.add('openOrCreateRestProject', (_args) => { const args = Object.assign({new: false}, _args) diff --git a/cypress/support/page_objects/mainPage.js b/cypress/support/page_objects/mainPage.js index 394e717b6b..94c3797374 100644 --- a/cypress/support/page_objects/mainPage.js +++ b/cypress/support/page_objects/mainPage.js @@ -26,18 +26,11 @@ export class _mainPage { } toolBarTopLeft(toolBarItem) { - - cy.get('header.v-toolbar').eq(0).find('a').then((obj) => { - cy.wrap(obj).eq(toolBarItem).click() - }) + return cy.get('header.v-toolbar').eq(0).find('a').eq(toolBarItem) } toolBarTopRight(toolBarItem) { - - cy.get('header.v-toolbar').eq(0).find('button').then((obj) => { - cy.wrap(obj).eq(toolBarItem).click() - cy.wait(500) - }) + return cy.get('header.v-toolbar').eq(0).find('button').eq(toolBarItem) } @@ -47,6 +40,64 @@ export class _mainPage { else return cy.get('.nc-nav-drawer').find('.v-list > .v-list-item').eq(item) } + + + // add new user to specified role + // + addNewUserToProject = (userCred, roleType) => { + + let linkText + + // click on New User button, feed details + cy.get('button:contains("New User")').first().click() + cy.get('label:contains("Email")').next('input').type(userCred.username).trigger('input') + cy.get('label:contains("Select User roles")').click() + + // opt-in requested role & submit + // note that, 'editor' is set by default + // + cy.getActiveMenu().contains(roleType).click() + cy.getActiveMenu().contains('editor').click() + cy.get('.mdi-menu-down').click() + cy.get('.nc-invite-or-save-btn').click() + + // get URL, invoke + cy.getActiveModal().find('.v-alert').then(($obj) => { + linkText = $obj.text() + cy.log(linkText) + + cy.visit(linkText) + + cy.wait(3000) + + // Redirected to new URL, feed details + // + cy.get('input[type="text"]').type(userCred.username) + cy.get('input[type="password"]').type(userCred.password) + cy.get('button:contains("SIGN UP")').click() + + cy.url({ timeout: 6000 }).should('contain', '#/project') + cy.wait(1000) + }) + } + + addExistingUserToProject = (emailId, role) => { + + cy.get('.v-list-item:contains("Team & Auth")').click() + cy.get(`tr:contains(${emailId})`).find('.mdi-plus', {timeout: 2000}).click() + cy.get(`tr:contains(${emailId})`).find('.mdi-pencil-outline', {timeout: 2000}).click() + + cy.get('label:contains(Select User roles)').click() + + // opt-in requested role & submit + // note that, 'editor' is set by default + // + cy.getActiveMenu().contains(role).click() + cy.getActiveMenu().contains('editor').click() + cy.get('.mdi-menu-down').click() + cy.get('.nc-invite-or-save-btn').click() + cy.wait(1000) + } } diff --git a/cypress/support/page_objects/navigation.js b/cypress/support/page_objects/navigation.js index 5193a920fd..35a69115b4 100644 --- a/cypress/support/page_objects/navigation.js +++ b/cypress/support/page_objects/navigation.js @@ -1,3 +1,6 @@ + +import { defaultDbParams } from "./projectConstants" + /////////////////////////////////////////////////////////// // Sign in/ Sign up page @@ -23,7 +26,7 @@ export class _loginPage { signIn(userCredentials) { this.go(urlPool.ncUrlSignIn) - cy.get('input[type="text"]').type(userCredentials.username) + cy.get('input[type="text"]', {timeout: 6000}).type(userCredentials.username) cy.get('input[type="password"]').type(userCredentials.password) cy.get('button:contains("SIGN IN")').click() @@ -35,7 +38,7 @@ export class _loginPage { signUp(userCredentials) { this.go(urlPool.ncUrlSignUp) - cy.get('input[type="text"]').type(userCredentials.username) + cy.get('input[type="text"]', {timeout: 6000}).type(userCredentials.username) cy.get('input[type="password"]').type(userCredentials.password) cy.get('button:contains("SIGN UP")').click() @@ -54,15 +57,6 @@ export class _loginPage { /////////////////////////////////////////////////////////// // Projects page -// DB type -const NC_DB_NONE = 0 -const NC_DB_EXISTING = 1 - -// API type -const NC_REST = 0 -const NC_GQL = 1 - - export class _projectsPage { // Project creation options @@ -103,7 +97,7 @@ export class _projectsPage { // click on "New Project" cy.get(':nth-child(5) > .v-btn').click() - if (NC_DB_NONE == projectData.dbType) { + if ('none' == projectData.dbType) { // Subsequent form, select (+ Create) option cy.get('.nc-create-xc-db-project').click({ force: true }) @@ -112,7 +106,7 @@ export class _projectsPage { cy.get('.nc-metadb-project-name').type(projectName) // Radio button: defaults to NC_REST - if (NC_GQL == projectData.apiType) { + if ('GQL' == projectData.apiType) { cy.contains('GRAPHQL APIs').closest('label').click(); } @@ -124,6 +118,8 @@ export class _projectsPage { return projectName } + + // dbType == 'external' else { // Subsequent form, select (+ Create by connection to external database) option @@ -134,7 +130,7 @@ export class _projectsPage { cy.contains('Enter Project Name').parent().find('input').clear().type(projectName) // Radio button: defaults to NC_REST - if (NC_GQL == projectData.apiType) { + if ('GQL' == projectData.apiType) { cy.contains('GRAPHQL APIs').closest('label').click(); } @@ -162,6 +158,17 @@ export class _projectsPage { } } + // create REST default project (sakila DB) + // + createDefaulRestProject() { + return this.createProject({ dbType: 1, apiType: 0, name: '' }, defaultDbParams ) + } + + // create GraphQL default project (sakila DB) + // + createDefaultGraphQlProject() { + return this.createProject({ dbType: 1, apiType: 1, name: '' }, defaultDbParams ) + } // Click on refresh key on projects page // diff --git a/cypress/support/page_objects/projectConstants.js b/cypress/support/page_objects/projectConstants.js new file mode 100644 index 0000000000..0827ebaf87 --- /dev/null +++ b/cypress/support/page_objects/projectConstants.js @@ -0,0 +1,65 @@ + +export const defaultDbParams = { + databaseType: 0, // MySQL + hostAddress: 'localhost', + portNumber: '3306', + username: 'root', + password: 'password', + databaseName: 'sakila' +} + +// database +// validation details +// advSettings: left navigation bar (audit, metadata, auth, transient view modes) +// editSchema: create table, add/update/delete column +// editData: add/ update/ delete row, cell contents +// editComment: add comment +// shareView: right navigation bar (share options) +export const roles = { + owner: { + name: 'owner', + credentials: { username: 'user@nocodb.com', password: 'Password123.' }, + validations: { advSettings: true, editSchema: true, editData: true, editComment: true, shareView: true } + }, + creator: { + name: 'creator', + credentials: { username: 'creator@nocodb.com', password: 'Password123.' }, + validations: { advSettings: true, editSchema: true, editData: true, editComment: true, shareView: true } + }, + editor: { + name: 'editor', + credentials: { username: 'editor@nocodb.com', password: 'Password123.' }, + validations: { advSettings: false, editSchema: false, editData: true, editComment: true, shareView: false } + }, + commenter: { + name: 'commenter', + credentials: { username: 'commenter@nocodb.com', password: 'Password123.' }, + validations: { advSettings: false, editSchema: false, editData: false, editComment: true, shareView: false } + }, + viewer: { + name: 'viewer', + credentials: { username: 'viewer@nocodb.com', password: 'Password123.' }, + validations: { advSettings: false, editSchema: false, editData: false, editComment: false, shareView: false } + } +} + +// default projects +// +export const staticProjects = { + sampleREST: { + basic: { dbType: 'none', apiType: 'REST', name: 'sampleREST' }, + config: {} + }, + sampleGQL: { + basic: { dbType: 'none', apiType: 'GQL', name: 'sampleGQL' }, + config: {} + }, + externalREST: { + basic: { dbType: 'external', apiType: 'REST', name: 'externalREST' }, + config: defaultDbParams + }, + externalGQL: { + basic: { dbType: 'external', apiType: 'GQL', name: 'externalGQL' }, + config: defaultDbParams + } +} \ No newline at end of file diff --git a/markdown/launch/brand-migration.md b/markdown/launch/brand-migration.md deleted file mode 100644 index ae45375e33..0000000000 --- a/markdown/launch/brand-migration.md +++ /dev/null @@ -1,27 +0,0 @@ -# Start - -# Social Media -- Twitter -- Youtube -- Discord - - -# Github - -## Github org rename -- rename xgenecloud into nocodb - -## Create xgenecloud organisation -- migrate repos : - - xc-seed-rest, xc-seed-gql, xc-desktop-app - - xgenecloud - -## Incorporate xmysql to nocodb -- Move xmysql to nocodb -- rename it to nocodb -- make a commit mentioning xmysql will be nocodb -- commit xgenecloud-ts into nocodb -- create roadmap -- remove/close issues -- remove/close pull requests -- headings diff --git a/markdown/launch/ph.md b/markdown/launch/ph.md deleted file mode 100644 index 02534752e8..0000000000 --- a/markdown/launch/ph.md +++ /dev/null @@ -1,91 +0,0 @@ -Hey Everybody, Hey Everybody 👋 👋 - -We are thrilled to open source NocoDB today. - -NocoDB is an open source Airtable alternative. - -Github : https://github.com/nocodb/nocodb - -NocoDB works by connecting to any relational databases and transforming them into a smart spreadsheet interface! This allows you to build no-code applications collaboratively with teams. NocoDB currently works with MySQL,PostgreSQL, SQL Server, SQLite, Amazon Aurora & MariaDB databases. - -Also NocoDB's app store allows you to build business workflows on views with combination of Slack, Microsoft Teams, Discord, Twilio, Whatsapp, Email to name a few. Plus NocoDB's provides programmatic access to APIs so that you can build integrations with Zapier / Integromat and custom applications too. - -- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - FEATURES : -Rich Spreadsheet Interface -⚡ Sort, search, filter, hide columns with ease -⚡ Create Views : Grid, Gallery, Kanban, Gantt, Form -⚡ Share Views : public & password protected -⚡ Personal & locked Views -⚡ Upload images to cells (Works with S3, Minio, GCP, Azure & many more)!! -⚡ Roles : Owner, Creator, Editor, Commenter, Viewer, Commenter, Custom. -⚡ Access Control : Fine-grained access control even to column level. -⚡ API tokens to integrate with Zapier / Integromat. - -Automations & App store : -⚡ Chat :Microsoft Teams, Slack, Discord, Mattermost -⚡ Email : SMTP, SES, Mailchimp -⚡ SMS : Twilio -⚡ Whatsapp -⚡ Any 3rd Party APIs - -Programmatic API access via -⚡ REST APIs (Swagger) -⚡ GraphQL APIs. -⚡ Includes JWT Authentication & Social Auth - -Coming Soon : Page designer, Mobile Apps, Amazing++ things. - -Follow our roadmap on Github : Star us / watch us / fork us - -- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -🎯  Why we building this ? -Most internet businesses equip themselves with either spreadsheet or a database to solve their business needs. Spreadsheets are used by a Billion+ humans collaboratively every single day. However, we are way off working at same speed on databases which are way more powerful tools when it comes to computing. Attempts to solve this with SaaS offerings have meant horrible access controls, vendor lockin, data lockin, abrupt price changes & mostly importantly a glass ceiling on what's possible in future. - - ❤️   Our Mission : -Our mission is to provide the most powerful no-code tool for databases as open source to every single internet business in the world. This would not only democratise collaborative app building but also bring forth a revolution where a billion+ people will have radical tinkering-and-building abilities on internet. - -- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -🔦  Also how it all began .. -We open sourced two API repos before NocoDB : -⚡ A no-code REST APIs generator for any MySQL DB. ~200,000 Docker pulls. This was a hobby project & had no GUI. 📈 -⚡ A low-code REST-GraphQL APIs generator for any database with GUI. Used by 100s of companies. Including fortune 500s & publicly trading companies. 📈 - -The thing that surprised us the most was that even non-developers started using our API products & rooting for us. 🚀 🚀  Whilst everybody loved instant API access to databases, it was slow-and-painful for them to build UI and collaborate with their teams. 💔 💔 ... This made us to rethink, radically re-combine the power of our 2 API products and transform them into something better. - -- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -💊  And ... There is ... One last thing ... this is insanely good : -NocoDB can be boiled down to just SIX lines of node.js. So just one file. With six lines of voodoo in it. So, it can run even on a smallest of servers with minimal setup!! Hope you liked it. - -- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -Join our community on : -📣   Github : https://github.com/nocodb/nocodb -📣   Discord : https://discord.gg/5RgZmkW -📣   Twitter : https://twitter.com/nocodb - -Would love to hear your usecases & feedbacks. 🙌 - -Happy hackery, -Team NocoDB - - - - -## Images -Todo -- Prepare images with headings - - Rich spreadsheet - - Upload images - - Various views - - Connect to any API - - How it works - - Web Hooks - - Headless APIs -- Prepare GIFS with headings -- Prepare short youtube video - diff --git a/markdown/plugins/google-auth.md b/markdown/plugins/google-auth.md index 527ef13714..2430dc031f 100644 --- a/markdown/plugins/google-auth.md +++ b/markdown/plugins/google-auth.md @@ -1,25 +1,12 @@ # Google Oauth - -![Step 1](../../static/google-oauth/1.png) - -![Step 2](../../static/google-oauth/2.png) - -![Step 3](../../static/google-oauth/3.png) - -![Step 4](../../static/google-oauth/4.png) - -![Step 1](../../static/google-oauth/1.png) - -![Step 5](../../static/google-oauth/5.png) - -![Step 6](../../static/google-oauth/6.png) - -![Step 7](../../static/google-oauth/7.png) - -![Step 8](../../static/google-oauth/8.png) - -![Step 9](../../static/google-oauth/9.png) - -![Step 10](../../static/google-oauth/10.png) - +1 +2 +3 +4 +5 +6 +7 +8 +9 +10 diff --git a/markdown/plugins/s3.md b/markdown/plugins/s3.md index b54222182e..61e0ad1704 100644 --- a/markdown/plugins/s3.md +++ b/markdown/plugins/s3.md @@ -1,22 +1,11 @@ # AWS S3 - -![Step 1](../../static/s3/1.png) - -![Step 2](../../static/s3/2.png) - -![Step 3](../../static/s3/3.png) - -![Step 4](../../static/s3/4.png) - -![Step 1](../../static/s3/1.png) - -![Step 5](../../static/s3/5.png) - -![Step 6](../../static/s3/6.png) - -![Step 7](../../static/s3/7.png) - -![Step 8](../../static/s3/8.png) - -![Step 9](../../static/s3/9.png) +1 +2 +3 +4 +5 +6 +7 +8 +9 diff --git a/packages/nc-gui/components/project/spreadsheet/components/spreadsheetNavDrawer.vue b/packages/nc-gui/components/project/spreadsheet/components/spreadsheetNavDrawer.vue index fab4dfc8eb..a6fae341d1 100644 --- a/packages/nc-gui/components/project/spreadsheet/components/spreadsheetNavDrawer.vue +++ b/packages/nc-gui/components/project/spreadsheet/components/spreadsheetNavDrawer.vue @@ -189,94 +189,94 @@ {{ $t('nav_drawer.virtual_views.gallery.create') }} - - - - {{ $t('nav_drawer.virtual_views.calendar.create') }} - - - - - {{ $t('nav_drawer.virtual_views.kanban.create') }} - - - - - {{ $t('nav_drawer.virtual_views.form.create') }} - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -378,19 +378,19 @@ - - - - - - - - - - - - - + + + + {{ $t('nav_drawer.advanced.views_list') }} +