Browse Source

qrcode column type: prototyping

pull/4142/head
Daniel Spaude 2 years ago
parent
commit
60b02fd6c6
No known key found for this signature in database
GPG Key ID: 654A3D1FA4F35FFE
  1. 1
      packages/nc-gui/components/smartsheet/Cell.vue
  2. 3
      packages/nc-gui/components/smartsheet/VirtualCell.vue
  3. 1
      packages/nc-gui/components/smartsheet/column/EditOrAdd.vue
  4. 66
      packages/nc-gui/components/smartsheet/column/QrCodeOptions.vue
  5. 3
      packages/nc-gui/components/smartsheet/header/VirtualCellIcon.vue
  6. 15
      packages/nc-gui/components/virtual-cell/QrCode.vue
  7. 2
      packages/nc-gui/composables/useVirtualCell.ts
  8. 1
      packages/nc-gui/lang/de.json
  9. 1
      packages/nc-gui/lang/en.json
  10. 6
      packages/nc-gui/utils/columnUtils.ts
  11. 4445
      packages/nocodb-sdk/package-lock.json
  12. 2
      packages/nocodb-sdk/src/lib/UITypes.ts
  13. 12
      packages/nocodb/src/lib/db/sql-data-mapper/lib/sql/BaseModelSqlv2.ts
  14. 13
      packages/nocodb/src/lib/meta/api/columnApis.ts
  15. 2
      packages/nocodb/src/lib/meta/api/dataApis/dataAliasApis.ts
  16. 1
      packages/nocodb/src/lib/meta/api/dataApis/dataApis.ts
  17. 1
      packages/nocodb/src/lib/meta/api/publicApis/publicDataApis.ts
  18. 4
      packages/nocodb/src/lib/migrations/XcMigrationSourcev2.ts
  19. 26
      packages/nocodb/src/lib/migrations/v2/nc_021_qr_code_column_type.ts
  20. 35
      packages/nocodb/src/lib/models/Column.ts
  21. 68
      packages/nocodb/src/lib/models/QrCodeColumn.ts
  22. 2
      packages/nocodb/src/lib/utils/globals.ts

1
packages/nc-gui/components/smartsheet/Cell.vue

@ -80,6 +80,7 @@ const {
isRating,
isCurrency,
isAttachment,
isQrCode,
isTextArea,
isString,
isInt,

3
packages/nc-gui/components/smartsheet/VirtualCell.vue

@ -24,7 +24,7 @@ provide(CellValueInj, toRef(props, 'modelValue'))
const isForm = inject(IsFormInj, ref(false))
const { isLookup, isBt, isRollup, isMm, isHm, isFormula, isCount } = useVirtualCell(column)
const { isLookup, isBt, isRollup, isMm, isHm, isFormula, isQrCode, isCount } = useVirtualCell(column)
function onNavigate(dir: NavigateDir, e: KeyboardEvent) {
emit('navigate', dir)
@ -44,6 +44,7 @@ function onNavigate(dir: NavigateDir, e: KeyboardEvent) {
<LazyVirtualCellBelongsTo v-else-if="isBt" />
<LazyVirtualCellRollup v-else-if="isRollup" />
<LazyVirtualCellFormula v-else-if="isFormula" />
<LazyVirtualCellQrCode v-else-if="isQrCode" />
<LazyVirtualCellCount v-else-if="isCount" />
<LazyVirtualCellLookup v-else-if="isLookup" />
</div>

1
packages/nc-gui/components/smartsheet/column/EditOrAdd.vue

@ -158,6 +158,7 @@ onMounted(() => {
</a-form-item>
<LazySmartsheetColumnFormulaOptions v-if="formState.uidt === UITypes.Formula" v-model:value="formState" />
<LazySmartsheetColumnQrCodeOptions v-if="formState.uidt === UITypes.QrCode" v-model:value="formState" />
<LazySmartsheetColumnCurrencyOptions v-if="formState.uidt === UITypes.Currency" v-model:value="formState" />
<LazySmartsheetColumnDurationOptions v-if="formState.uidt === UITypes.Duration" v-model:value="formState" />
<LazySmartsheetColumnRatingOptions v-if="formState.uidt === UITypes.Rating" v-model:value="formState" />

66
packages/nc-gui/components/smartsheet/column/QrCodeOptions.vue

@ -0,0 +1,66 @@
<script setup lang="ts">
import { UITypes } from 'nocodb-sdk'
import type { SelectProps } from 'ant-design-vue'
import { useVModel } from '#imports'
const props = defineProps<{
value: any
}>()
const emit = defineEmits(['update:value'])
const meta = inject(MetaInj, ref())
const activeView = inject(ActiveViewInj, ref())
const reloadDataHook = inject(ReloadViewDataHookInj)!
const { fields, metaColumnById } = useViewColumns(activeView, meta, () => reloadDataHook.trigger())
const vModel = useVModel(props, 'value', emit)
const { column } = useColumnCreateStoreOrThrow()
const allowedColumnTypesForQrValue = [UITypes.Formula, UITypes.SingleLineText, UITypes.LongText] as string[]
const columnsAllowedAsQrValue = computed<SelectProps['options']>(() => {
return fields.value
?.filter((el) => el.fk_column_id && allowedColumnTypesForQrValue.includes(metaColumnById.value[el.fk_column_id].uidt))
.map((field) => {
return {
value: field.fk_column_id,
label: field.title,
}
})
})
// set default value
vModel.value.fk_qr_value_column_id = (column?.value?.colOptions as Record<string, any>)?.fk_qr_value_column_id || ''
// console.log('vModel.value.fk_qr_value_column_id', vModel.value.fk_qr_value_column_id)
const jsonstr = computed(() => {
return JSON.stringify(vModel.value, null, 2)
})
</script>
<template>
<a-row>
<a-col :span="24">
<textarea v-model="jsonstr" rows="8" cols="40"></textarea>
<div>
<span class="font-bold"> {{ $t('labels.chooseQrValueColumn') }}</span>
<a-divider class="!my-2" />
</div>
<div class="nc-fields-list py-1">
<div class="grouping-field">
<a-select
v-model:value="vModel.fk_qr_value_column_id"
class="w-full nc-kanban-grouping-field-select"
:options="columnsAllowedAsQrValue"
placeholder="Select a Grouping Field"
@click.stop
/>
</div>
</div>
</a-col>
</a-row>
</template>

3
packages/nc-gui/components/smartsheet/header/VirtualCellIcon.vue

@ -8,6 +8,7 @@ import HMIcon from '~icons/mdi/table-arrow-right'
import BTIcon from '~icons/mdi/table-arrow-left'
import MMIcon from '~icons/mdi/table-network'
import FormulaIcon from '~icons/mdi/math-integral'
import QrCodeScan from '~icons/mdi/qrcode-scan'
import RollupIcon from '~icons/mdi/movie-roll'
import CountIcon from '~icons/mdi/counter'
import SpecificDBTypeIcon from '~icons/mdi/database-settings'
@ -49,6 +50,8 @@ const icon = computed(() => {
return { icon: SpecificDBTypeIcon, color: 'text-grey' }
case UITypes.Formula:
return { icon: FormulaIcon, color: 'text-grey' }
case UITypes.QrCode:
return { icon: QrCodeScan, color: 'text-grey' }
case UITypes.Lookup:
switch ((relationColumn?.colOptions as LinkToAnotherRecordType)?.type) {
case RelationTypes.MANY_TO_MANY:

15
packages/nc-gui/components/virtual-cell/QrCode.vue

@ -0,0 +1,15 @@
<script setup lang="ts">
import { useQRCode } from '@vueuse/integrations/useQRCode'
const value = inject(CellValueInj)
const qrCode =
value?.value &&
useQRCode(value?.value, {
width: 150,
})
</script>
<template>
<img v-if="qrCode" :src="qrCode" alt="QR Code" class="qr-code" />
</template>

2
packages/nc-gui/composables/useVirtualCell.ts

@ -23,6 +23,7 @@ export function useVirtualCell(column: Ref<ColumnType | undefined>) {
const isRollup = computed(() => column.value?.uidt === UITypes.Rollup)
const isFormula = computed(() => column.value?.uidt === UITypes.Formula)
const isCount = computed(() => column.value?.uidt === UITypes.Count)
const isQrCode = computed(() => column.value?.uidt === UITypes.QrCode)
return {
isHm,
@ -32,5 +33,6 @@ export function useVirtualCell(column: Ref<ColumnType | undefined>) {
isRollup,
isFormula,
isCount,
isQrCode,
}
}

1
packages/nc-gui/lang/de.json

@ -238,6 +238,7 @@
"created": "Erstellt",
"sqlOutput": "SQL-Ausgabe",
"addOption": "Option hinzufügen",
"chooseQrValueColumn": "Spalte für den QR-Code-Inhalt wählen",
"aggregateFunction": "Globale Funktion",
"dbCreateIfNotExists": "Datenbank: Erstellen, falls nicht vorhanden",
"clientKey": "Client-Schlüssel",

1
packages/nc-gui/lang/en.json

@ -238,6 +238,7 @@
"created": "Created",
"sqlOutput": "SQL Output",
"addOption": "Add option",
"chooseQrValueColumn": "Column to use for the QR code value",
"aggregateFunction": "Aggregate function",
"dbCreateIfNotExists": "Database : create if not exists",
"clientKey": "Client Key",

6
packages/nc-gui/utils/columnUtils.ts

@ -2,6 +2,7 @@ import type { ColumnType, LinkToAnotherRecordType } from 'nocodb-sdk'
import { RelationTypes, UITypes } from 'nocodb-sdk'
import LinkVariant from '~icons/mdi/link-variant'
import TableColumnPlusBefore from '~icons/mdi/table-column-plus-before'
import QrCodeScan from '~icons/mdi/qrcode-scan'
import FormatColorText from '~icons/mdi/format-color-text'
import TextSubject from '~icons/mdi/text-subject'
import JSONIcon from '~icons/mdi/code-json'
@ -35,6 +36,11 @@ const uiTypes = [
icon: LinkVariant,
virtual: 1,
},
{
name: UITypes.QrCode,
icon: QrCodeScan,
virtual: 1,
},
{
name: UITypes.Lookup,
icon: TableColumnPlusBefore,

4445
packages/nocodb-sdk/package-lock.json generated

File diff suppressed because it is too large Load Diff

2
packages/nocodb-sdk/src/lib/UITypes.ts

@ -35,6 +35,7 @@ enum UITypes {
JSON = 'JSON',
SpecificDBType = 'SpecificDBType',
Barcode = 'Barcode',
QrCode = 'QrCode',
Button = 'Button',
}
@ -50,6 +51,7 @@ export function isVirtualCol(
// UITypes.SpecificDBType,
UITypes.LinkToAnotherRecord,
UITypes.Formula,
UITypes.QrCode,
UITypes.Rollup,
UITypes.Lookup,
// UITypes.Count,

12
packages/nocodb/src/lib/db/sql-data-mapper/lib/sql/BaseModelSqlv2.ts

@ -44,6 +44,7 @@ import { NcError } from '../../../../meta/helpers/catchError';
import { customAlphabet } from 'nanoid';
import DOMPurify from 'isomorphic-dompurify';
import { sanitize, unsanitize } from './helpers/sanitize';
import QrCodeColumn from '../../../../models/QrCodeColumn';
const GROUP_COL = '__nc_group_id';
@ -1360,6 +1361,16 @@ class BaseModelSqlv2 {
case 'LinkToAnotherRecord':
case 'Lookup':
break;
case 'QrCode': {
const qrCodeColumn = await column.getColOptions<QrCodeColumn>();
const qrValueColumn = await Column.get({
colId: qrCodeColumn.fk_qr_value_column_id,
});
console.log('qrCodeColumn', qrCodeColumn);
console.log('qrValueColumn', qrValueColumn);
qb.select({ [column.column_name]: qrValueColumn.column_name });
break;
}
case 'Formula':
{
const formula = await column.getColOptions<FormulaColumn>();
@ -2097,6 +2108,7 @@ class BaseModelSqlv2 {
f.uidt !== UITypes.Rollup &&
f.uidt !== UITypes.Lookup &&
f.uidt !== UITypes.Formula &&
f.uidt !== UITypes.QrCode &&
f.uidt !== UITypes.SpecificDBType
)
.sort(

13
packages/nocodb/src/lib/meta/api/columnApis.ts

@ -715,11 +715,20 @@ export async function columnUpdate(req: Request, res: Response<TableType>) {
UITypes.Rollup,
UITypes.LinkToAnotherRecord,
UITypes.Formula,
UITypes.QrCode,
UITypes.ForeignKey,
].includes(column.uidt)
) {
if (column.uidt === colBody.uidt) {
if (column.uidt === UITypes.Formula) {
if (column.uidt === UITypes.QrCode) {
console.debug('DEBUG - QR CODE UPDATE - req.body', req.body);
// TODO: probably have to continue here
//
await Column.update(column.id, {
...column,
...colBody,
});
} else if (column.uidt === UITypes.Formula) {
colBody.formula = await substituteColumnAliasWithIdInFormula(
colBody.formula_raw || colBody.formula,
table.columns
@ -745,6 +754,7 @@ export async function columnUpdate(req: Request, res: Response<TableType>) {
UITypes.Rollup,
UITypes.LinkToAnotherRecord,
UITypes.Formula,
UITypes.QrCode,
UITypes.ForeignKey,
].includes(colBody.uidt)
) {
@ -1431,6 +1441,7 @@ export async function columnDelete(req: Request, res: Response<TableType>) {
switch (column.uidt) {
case UITypes.Lookup:
case UITypes.Rollup:
case UITypes.QrCode:
case UITypes.Formula:
await Column.delete(req.params.columnId);
break;

2
packages/nocodb/src/lib/meta/api/dataApis/dataAliasApis.ts

@ -13,6 +13,7 @@ import getAst from '../../../db/sql-data-mapper/lib/sql/helpers/getAst';
// todo: Handle the error case where view doesnt belong to model
async function dataList(req: Request, res: Response) {
// BOOKMARK
const { model, view } = await getViewAndModelFromRequestByAliasOrId(req);
res.json(await getDataList(model, view, req));
}
@ -128,6 +129,7 @@ async function getDataList(model, view: View, req) {
// e.g. search some text in a numeric field
}
// BOOKMARK
return new PagedResponseImpl(data, {
...req.query,
count,

1
packages/nocodb/src/lib/meta/api/dataApis/dataApis.ts

@ -11,6 +11,7 @@ import apiMetrics from '../../helpers/apiMetrics';
import getAst from '../../../db/sql-data-mapper/lib/sql/helpers/getAst';
export async function dataList(req: Request, res: Response, next) {
// BOOKMARK
const view = await View.get(req.params.viewId);
const model = await Model.getByIdOrName({

1
packages/nocodb/src/lib/meta/api/publicApis/publicDataApis.ts

@ -70,6 +70,7 @@ export async function dataList(req: Request, res: Response) {
{},
listArgs
);
// BOOKMARK
count = await baseModel.count(listArgs);
} catch (e) {
// show empty result instead of throwing error here

4
packages/nocodb/src/lib/migrations/XcMigrationSourcev2.ts

@ -8,6 +8,7 @@ import * as nc_017_add_user_token_version_column from './v2/nc_017_add_user_toke
import * as nc_018_add_meta_in_view from './v2/nc_018_add_meta_in_view';
import * as nc_019_add_meta_in_meta_tables from './v2/nc_019_add_meta_in_meta_tables';
import * as nc_020_kanban_view from './v2/nc_020_kanban_view';
import * as nc_021_qr_code_column_type from './v2/nc_021_qr_code_column_type';
// Create a custom migration source class
export default class XcMigrationSourcev2 {
@ -27,6 +28,7 @@ export default class XcMigrationSourcev2 {
'nc_018_add_meta_in_view',
'nc_019_add_meta_in_meta_tables',
'nc_020_kanban_view',
'nc_021_qr_code_column_type',
]);
}
@ -56,6 +58,8 @@ export default class XcMigrationSourcev2 {
return nc_019_add_meta_in_meta_tables;
case 'nc_020_kanban_view':
return nc_020_kanban_view;
case 'nc_021_qr_code_column_type':
return nc_021_qr_code_column_type;
}
}
}

26
packages/nocodb/src/lib/migrations/v2/nc_021_qr_code_column_type.ts

@ -0,0 +1,26 @@
import Knex from 'knex';
import { MetaTable } from '../../utils/globals';
const up = async (knex: Knex) => {
await knex.schema.createTable(MetaTable.COL_QRCODE, (table) => {
table.string('id', 20).primary().notNullable();
table.string('fk_column_id', 20);
table.foreign('fk_column_id').references(`${MetaTable.COLUMNS}.id`);
table.string('fk_qr_value_column_id', 20);
table
.foreign('fk_qr_value_column_id')
.references(`${MetaTable.COLUMNS}.id`);
table.boolean('deleted');
table.float('order');
table.timestamps(true, true);
});
};
const down = async (knex: Knex) => {
await knex.schema.dropTable(MetaTable.COL_QRCODE);
};
export { up, down };

35
packages/nocodb/src/lib/models/Column.ts

@ -18,6 +18,7 @@ import Sort from './Sort';
import Filter from './Filter';
import addFormulaErrorIfMissingColumn from '../meta/helpers/addFormulaErrorIfMissingColumn';
import { NcError } from '../meta/helpers/catchError';
import QrCodeColumn from './QrCodeColumn';
export default class Column<T = any> implements ColumnType {
public fk_model_id: string;
@ -219,6 +220,20 @@ export default class Column<T = any> implements ColumnType {
);
break;
}
case UITypes.QrCode: {
// BOOKMARK
await QrCodeColumn.insert(
{
fk_column_id: colId,
// TODO: probably do here (or maybe before - in columnApis)
// substitutions / special handling of virtual columns
// (similar to how e.g. Formular is doing it)
fk_qr_value_column_id: column.fk_qr_value_column_id,
},
ncMeta
);
break;
}
case UITypes.Formula: {
await FormulaColumn.insert(
{
@ -395,6 +410,9 @@ export default class Column<T = any> implements ColumnType {
case UITypes.Formula:
res = await FormulaColumn.read(this.id, ncMeta);
break;
case UITypes.QrCode:
res = await QrCodeColumn.read(this.id, ncMeta);
break;
// default:
// res = await DbColumn.read(this.id);
// break;
@ -508,6 +526,7 @@ export default class Column<T = any> implements ColumnType {
return columns.map(c => new Column(c));*/
}
// BOOKMARK
public static async get(
{
base_id,
@ -691,6 +710,10 @@ export default class Column<T = any> implements ColumnType {
colOptionTableName = MetaTable.COL_FORMULA;
cacheScopeName = CacheScope.COL_FORMULA;
break;
case UITypes.QrCode:
colOptionTableName = MetaTable.COL_QRCODE;
cacheScopeName = CacheScope.COL_QRCODE;
break;
}
if (colOptionTableName && cacheScopeName) {
@ -855,6 +878,18 @@ export default class Column<T = any> implements ColumnType {
);
break;
}
case UITypes.QrCode: {
await ncMeta.metaDelete(null, null, MetaTable.COL_QRCODE, {
fk_column_id: colId,
});
await NocoCache.deepDel(
CacheScope.COL_QRCODE,
`${CacheScope.COL_QRCODE}:${colId}`,
CacheDelDirection.CHILD_TO_PARENT
);
break;
}
case UITypes.MultiSelect:
case UITypes.SingleSelect: {

68
packages/nocodb/src/lib/models/QrCodeColumn.ts

@ -0,0 +1,68 @@
import Noco from '../Noco';
import { CacheGetType, CacheScope, MetaTable } from '../utils/globals';
import NocoCache from '../cache/NocoCache';
import { extractProps } from '../meta/helpers/extractProps';
export default class QrCodeColumn {
fk_column_id: string;
fk_qr_value_column_id: string;
constructor(data: Partial<QrCodeColumn>) {
Object.assign(this, data);
}
public static async insert(
data: Partial<QrCodeColumn>,
ncMeta = Noco.ncMeta
) {
// BOOKMARK
await ncMeta.metaInsert2(null, null, MetaTable.COL_QRCODE, {
fk_column_id: data.fk_column_id,
fk_qr_value_column_id: data.fk_qr_value_column_id,
});
return this.read(data.fk_column_id, ncMeta);
}
public static async read(columnId: string, ncMeta = Noco.ncMeta) {
let column =
columnId &&
(await NocoCache.get(
`${CacheScope.COL_QRCODE}:${columnId}`,
CacheGetType.TYPE_OBJECT
));
if (!column) {
column = await ncMeta.metaGet2(
null, //,
null, //model.db_alias,
MetaTable.COL_QRCODE,
{ fk_column_id: columnId }
);
await NocoCache.set(`${CacheScope.COL_QRCODE}:${columnId}`, column);
}
return column ? new QrCodeColumn(column) : null;
}
id: string;
static async update(
id: string,
qrCode: Partial<QrCodeColumn>,
ncMeta = Noco.ncMeta
) {
const updateObj = extractProps(qrCode, [
'fk_column_id',
'fk_qr_value_column_id',
]);
// get existing cache
const key = `${CacheScope.COL_QRCODE}:${id}`;
let o = await NocoCache.get(key, CacheGetType.TYPE_OBJECT);
if (o) {
o = { ...o, ...updateObj };
// set cache
await NocoCache.set(key, o);
}
// set meta
await ncMeta.metaUpdate(null, null, MetaTable.COL_QRCODE, updateObj, id);
}
}

2
packages/nocodb/src/lib/utils/globals.ts

@ -10,6 +10,7 @@ export enum MetaTable {
COL_LOOKUP = 'nc_col_lookup_v2',
COL_ROLLUP = 'nc_col_rollup_v2',
COL_FORMULA = 'nc_col_formula_v2',
COL_QRCODE = 'nc_col_qrcode_v2',
FILTER_EXP = 'nc_filter_exp_v2',
// HOOK_FILTER_EXP = 'nc_hook_filter_exp_v2',
SORT = 'nc_sort_v2',
@ -85,6 +86,7 @@ export enum CacheScope {
COL_LOOKUP = 'colLookup',
COL_ROLLUP = 'colRollup',
COL_FORMULA = 'colFormula',
COL_QRCODE = 'colQRCode',
FILTER_EXP = 'filterExp',
SORT = 'sort',
SHARED_VIEW = 'sharedView',

Loading…
Cancel
Save