diff --git a/packages/nc-gui/components/project/spreadsheet/components/FlipCard.vue b/packages/nc-gui/components/project/spreadsheet/components/FlipCard.vue new file mode 100644 index 0000000000..6085f053b7 --- /dev/null +++ b/packages/nc-gui/components/project/spreadsheet/components/FlipCard.vue @@ -0,0 +1,111 @@ + + + + + diff --git a/packages/nc-gui/components/project/spreadsheet/components/SpreadsheetNavDrawer.vue b/packages/nc-gui/components/project/spreadsheet/components/SpreadsheetNavDrawer.vue index 8e4788c7b7..9e7a849928 100644 --- a/packages/nc-gui/components/project/spreadsheet/components/SpreadsheetNavDrawer.vue +++ b/packages/nc-gui/components/project/spreadsheet/components/SpreadsheetNavDrawer.vue @@ -308,23 +308,37 @@ mdi-close-circle-outline - - - - - - 🚀 We are Hiring! 🚀 - - - + + + + @@ -443,10 +457,11 @@ import { copyTextToClipboard } from '~/helpers/xutils' import SponsorMini from '~/components/SponsorMini' import CodeSnippet from '~/components/project/spreadsheet/components/CodeSnippet' import WebhookSlider from '~/components/project/tableTabs/webhook/WebhookSlider' +import FlipCard from '~/components/project/spreadsheet/components/FlipCard' export default { name: 'SpreadsheetNavDrawer', - components: { WebhookSlider, CodeSnippet, SponsorMini, Extras, CreateViewDialog, draggable }, + components: { WebhookSlider, CodeSnippet, SponsorMini, Extras, CreateViewDialog, draggable, FlipCard }, props: { extraViewParams: Object, showAdvanceOptions: Boolean, @@ -570,6 +585,13 @@ export default { viewType = 'view' } return `${this.dashboardUrl}#/nc/${viewType}/${this.shareLink.uuid}` + }, + supportCost() { + const cost = parseInt(this.$store.getters['project/GtrProjectCost']) + if (cost > 0) { + return `This costs ~$${cost}/year in non-open source products.` + } + return 'Your donations will help us to make this product better.' } }, watch: { @@ -953,8 +975,8 @@ export default { .close-icon { position: absolute; - right: 10px; - top: 10px; + right: 4px; + top: 12px; z-index: 9; opacity: 0; transition: 0.4s opacity; diff --git a/packages/nc-gui/store/project.js b/packages/nc-gui/store/project.js index 9c8c57499e..ba701aac5b 100644 --- a/packages/nc-gui/store/project.js +++ b/packages/nc-gui/store/project.js @@ -102,7 +102,11 @@ export const mutations = { }, MutAppInfo(state, appInfo) { state.appInfo = appInfo - } + }, + MutProjectCost(state, cost) { + state.project.cost = cost + }, + } function getSerializedEnvObj(data) { @@ -293,6 +297,9 @@ export const getters = { && state.unserializedList[0].projectJson && state.unserializedList[0].projectJson.envs ? Object.keys(state.unserializedList[0].projectJson.envs) : [] }, + GtrProjectCost(state) { + return state.project.cost || 0 + }, } @@ -351,6 +358,11 @@ export const actions = { this.$ncApis.clear() this.$ncApis.setProjectId(projectId) } + + this.$api.project.cost(projectId).then(res => { + if (res.cost) commit('MutProjectCost', res.cost) + }) + } catch (e) { console.log(e) this.$toast.error(e).goAway(3000) diff --git a/packages/nocodb-sdk/src/lib/Api.ts b/packages/nocodb-sdk/src/lib/Api.ts index fd93432616..2c992d87d2 100644 --- a/packages/nocodb-sdk/src/lib/Api.ts +++ b/packages/nocodb-sdk/src/lib/Api.ts @@ -1311,6 +1311,23 @@ export class Api< ...params, }), + /** + * @description Project compare cost + * + * @tags Project + * @name Cost + * @summary Project compare cost + * @request GET:/api/v1/db/meta/projects/{projectId}/cost + * @response `200` `object` OK + */ + cost: (projectId: string, params: RequestParams = {}) => + this.request({ + path: `/api/v1/db/meta/projects/${projectId}/cost`, + method: 'GET', + format: 'json', + ...params, + }), + /** * No description * diff --git a/packages/nocodb/src/lib/meta/api/projectApis.ts b/packages/nocodb/src/lib/meta/api/projectApis.ts index 1b3a18c03c..95d0806767 100644 --- a/packages/nocodb/src/lib/meta/api/projectApis.ts +++ b/packages/nocodb/src/lib/meta/api/projectApis.ts @@ -390,6 +390,26 @@ export async function projectInfoGet(req, res) { }); } +export async function projectCost(req, res) { + let cost = 0 + const project = await Project.getWithInfo(req.params.projectId); + const sqlClient = NcConnectionMgrv2.getSqlClient(project.bases[0]); + const userCount = await ProjectUser.getUsersCount(req.query) + const recordCount = (await sqlClient.totalRecords())?.data.TotalRecords + + if (recordCount > 100000) { // 36,000 or $79/user/month + cost = Math.max(36000, 948 * userCount) + } else if (recordCount > 50000) { // $36,000 or $50/user/month + cost = Math.max(36000, 600 * userCount) + } else if (recordCount > 10000) { // $240/user/yr + cost = Math.min(240 * userCount, 36000) + } else if (recordCount > 1000) { // $120/user/yr + cost = Math.min(120 * userCount, 36000) + } + + res.json({ cost }); +} + export default router => { router.get( '/api/v1/db/meta/projects/:projectId/info', @@ -401,6 +421,11 @@ export default router => { metaApiMetrics, ncMetaAclMw(projectGet, 'projectGet') ); + router.get( + '/api/v1/db/meta/projects/:projectId/cost', + metaApiMetrics, + ncMetaAclMw(projectCost, 'projectCost') + ); router.delete( '/api/v1/db/meta/projects/:projectId', metaApiMetrics, diff --git a/scripts/sdk/swagger.json b/scripts/sdk/swagger.json index 2edd2c4d1b..acbac69bab 100644 --- a/scripts/sdk/swagger.json +++ b/scripts/sdk/swagger.json @@ -992,6 +992,49 @@ "description": "" } }, + "/api/v1/db/meta/projects/{projectId}/cost": { + "parameters": [ + { + "schema": { + "type": "string" + }, + "name": "projectId", + "in": "path", + "required": true + } + ], + "get": { + "summary": "Project compare cost", + "operationId": "project-cost", + "description": "Project compare cost", + "parameters": [ + { + "schema": { + "type": "string" + }, + "in": "header", + "name": "xc-auth", + "description": "Auth token" + } + ], + "tags": [ + "Project" + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": {} + } + } + } + } + } + } + }, "/api/v1/db/meta/projects/{projectId}/tables": { "parameters": [ {