mirror of https://github.com/nocodb/nocodb
Wing-Kam Wong
2 years ago
20 changed files with 759 additions and 11 deletions
@ -0,0 +1,38 @@
|
||||
<script setup lang="ts"> |
||||
import { dateFormats, timeFormats, useVModel } from '#imports' |
||||
|
||||
const props = defineProps<{ |
||||
value: any |
||||
}>() |
||||
|
||||
const emit = defineEmits(['update:value']) |
||||
|
||||
const vModel = useVModel(props, 'value', emit) |
||||
|
||||
if (!vModel.value.meta?.date_format) { |
||||
if (!vModel.value.meta) vModel.value.meta = {} |
||||
vModel.value.meta.date_format = dateFormats[0] |
||||
} |
||||
|
||||
if (!vModel.value.meta?.time_format) { |
||||
if (!vModel.value.meta) vModel.value.meta = {} |
||||
vModel.value.meta.time_format = timeFormats[0] |
||||
} |
||||
</script> |
||||
|
||||
<template> |
||||
<a-form-item label="Date Format"> |
||||
<a-select v-model:value="vModel.meta.date_format" class="nc-date-select" dropdown-class-name="nc-dropdown-date-format"> |
||||
<a-select-option v-for="(format, i) of dateFormats" :key="i" :value="format"> |
||||
{{ format }} |
||||
</a-select-option> |
||||
</a-select> |
||||
</a-form-item> |
||||
<a-form-item label="Time Format"> |
||||
<a-select v-model:value="vModel.meta.time_format" class="nc-time-select" dropdown-class-name="nc-dropdown-time-format"> |
||||
<a-select-option v-for="(format, i) of timeFormats" :key="i" :value="format"> |
||||
{{ format }} |
||||
</a-select-option> |
||||
</a-select> |
||||
</a-form-item> |
||||
</template> |
@ -0,0 +1,137 @@
|
||||
export function convertUnits( |
||||
unit: string, |
||||
type: 'mysql' | 'mssql' | 'pg' | 'sqlite' |
||||
) { |
||||
switch (unit) { |
||||
case 'milliseconds': |
||||
case 'ms': { |
||||
switch (type) { |
||||
case 'mssql': |
||||
return 'millisecond'; |
||||
case 'mysql': |
||||
// MySQL doesn't support millisecond
|
||||
// hence change from MICROSECOND to millisecond manually
|
||||
return 'MICROSECOND'; |
||||
case 'pg': |
||||
case 'sqlite': |
||||
return 'milliseconds'; |
||||
default: |
||||
return unit; |
||||
} |
||||
} |
||||
case 'seconds': |
||||
case 's': { |
||||
switch (type) { |
||||
case 'mssql': |
||||
case 'pg': |
||||
return 'second'; |
||||
case 'mysql': |
||||
return 'SECOND'; |
||||
case 'sqlite': |
||||
return 'seconds'; |
||||
default: |
||||
return unit; |
||||
} |
||||
} |
||||
case 'minutes': |
||||
case 'm': { |
||||
switch (type) { |
||||
case 'mssql': |
||||
case 'pg': |
||||
return 'minute'; |
||||
case 'mysql': |
||||
return 'MINUTE'; |
||||
case 'sqlite': |
||||
return 'minutes'; |
||||
default: |
||||
return unit; |
||||
} |
||||
} |
||||
case 'hours': |
||||
case 'h': { |
||||
switch (type) { |
||||
case 'mssql': |
||||
case 'pg': |
||||
return 'hour'; |
||||
case 'mysql': |
||||
return 'HOUR'; |
||||
case 'sqlite': |
||||
return 'hours'; |
||||
default: |
||||
return unit; |
||||
} |
||||
} |
||||
case 'days': |
||||
case 'd': { |
||||
switch (type) { |
||||
case 'mssql': |
||||
case 'pg': |
||||
return 'day'; |
||||
case 'mysql': |
||||
return 'DAY'; |
||||
case 'sqlite': |
||||
return 'days'; |
||||
default: |
||||
return unit; |
||||
} |
||||
} |
||||
case 'weeks': |
||||
case 'w': { |
||||
switch (type) { |
||||
case 'mssql': |
||||
case 'pg': |
||||
return 'week'; |
||||
case 'mysql': |
||||
return 'WEEK'; |
||||
case 'sqlite': |
||||
return 'weeks'; |
||||
default: |
||||
return unit; |
||||
} |
||||
} |
||||
case 'months': |
||||
case 'M': { |
||||
switch (type) { |
||||
case 'mssql': |
||||
case 'pg': |
||||
return 'month'; |
||||
case 'mysql': |
||||
return 'MONTH'; |
||||
case 'sqlite': |
||||
return 'months'; |
||||
default: |
||||
return unit; |
||||
} |
||||
} |
||||
case 'quarters': |
||||
case 'Q': { |
||||
switch (type) { |
||||
case 'mssql': |
||||
case 'pg': |
||||
return 'quarter'; |
||||
case 'mysql': |
||||
return 'QUARTER'; |
||||
case 'sqlite': |
||||
return 'quarters'; |
||||
default: |
||||
return unit; |
||||
} |
||||
} |
||||
case 'years': |
||||
case 'y': { |
||||
switch (type) { |
||||
case 'mssql': |
||||
case 'pg': |
||||
return 'year'; |
||||
case 'mysql': |
||||
return 'YEAR'; |
||||
case 'sqlite': |
||||
return 'years'; |
||||
default: |
||||
return unit; |
||||
} |
||||
} |
||||
default: |
||||
return unit; |
||||
} |
||||
} |
@ -0,0 +1,75 @@
|
||||
import dayjs from 'dayjs'; |
||||
|
||||
export const dateFormats = [ |
||||
'DD-MM-YYYY', |
||||
'MM-DD-YYYY', |
||||
'YYYY-MM-DD', |
||||
'DD/MM/YYYY', |
||||
'MM/DD/YYYY', |
||||
'YYYY/MM/DD', |
||||
'DD MM YYYY', |
||||
'MM DD YYYY', |
||||
'YYYY MM DD', |
||||
]; |
||||
|
||||
export function validateDateFormat(v: string) { |
||||
return dateFormats.includes(v); |
||||
} |
||||
|
||||
export function validateDateWithUnknownFormat(v: string) { |
||||
for (const format of dateFormats) { |
||||
if (dayjs(v, format, true).isValid() as any) { |
||||
return true; |
||||
} |
||||
for (const timeFormat of ['HH:mm', 'HH:mm:ss', 'HH:mm:ss.SSS']) { |
||||
if (dayjs(v, `${format} ${timeFormat}`, true).isValid() as any) { |
||||
return true; |
||||
} |
||||
} |
||||
} |
||||
return false; |
||||
} |
||||
|
||||
export function getDateFormat(v: string) { |
||||
for (const format of dateFormats) { |
||||
if (dayjs(v, format, true).isValid()) { |
||||
return format; |
||||
} |
||||
} |
||||
return 'YYYY/MM/DD'; |
||||
} |
||||
|
||||
export function getDateTimeFormat(v: string) { |
||||
for (const format of dateFormats) { |
||||
for (const timeFormat of ['HH:mm', 'HH:mm:ss', 'HH:mm:ss.SSS']) { |
||||
const dateTimeFormat = `${format} ${timeFormat}`; |
||||
if (dayjs(v, dateTimeFormat, true).isValid() as any) { |
||||
return dateTimeFormat; |
||||
} |
||||
} |
||||
} |
||||
return 'YYYY/MM/DD'; |
||||
} |
||||
|
||||
export function parseStringDate(v: string, dateFormat: string) { |
||||
const dayjsObj = dayjs(v); |
||||
if (dayjsObj.isValid()) { |
||||
v = dayjsObj.format('YYYY-MM-DD'); |
||||
} else { |
||||
v = dayjs(v, dateFormat).format('YYYY-MM-DD'); |
||||
} |
||||
return v; |
||||
} |
||||
|
||||
export function convertToTargetFormat( |
||||
v: string, |
||||
oldDataFormat, |
||||
newDateFormat: string |
||||
) { |
||||
if ( |
||||
!dateFormats.includes(oldDataFormat) || |
||||
!dateFormats.includes(newDateFormat) |
||||
) |
||||
return v; |
||||
return dayjs(v, oldDataFormat).format(newDateFormat); |
||||
} |
@ -0,0 +1,67 @@
|
||||
import { CellPageObject } from '.'; |
||||
import BasePage from '../../../Base'; |
||||
|
||||
export class DateTimeCellPageObject extends BasePage { |
||||
readonly cell: CellPageObject; |
||||
|
||||
constructor(cell: CellPageObject) { |
||||
super(cell.rootPage); |
||||
this.cell = cell; |
||||
} |
||||
|
||||
get({ index, columnHeader }: { index?: number; columnHeader: string }) { |
||||
return this.cell.get({ index, columnHeader }); |
||||
} |
||||
|
||||
async open({ index, columnHeader }: { index: number; columnHeader: string }) { |
||||
await this.rootPage.locator('.nc-grid-add-new-cell').click(); |
||||
|
||||
await this.cell.dblclick({ |
||||
index, |
||||
columnHeader, |
||||
}); |
||||
} |
||||
|
||||
async save() { |
||||
await this.rootPage.locator('button:has-text("Ok")').click(); |
||||
} |
||||
|
||||
async selectDate({ |
||||
// date formats in `YYYY-MM-DD`
|
||||
date, |
||||
}: { |
||||
date: string; |
||||
}) { |
||||
// title date format needs to be YYYY-MM-DD
|
||||
await this.rootPage.locator(`td[title="${date}"]`).click(); |
||||
} |
||||
|
||||
async selectTime({ |
||||
// hour: 0 - 23
|
||||
// minute: 0 - 59
|
||||
// second: 0 - 59
|
||||
hour, |
||||
minute, |
||||
second, |
||||
}: { |
||||
hour: number; |
||||
minute: number; |
||||
second?: number | null; |
||||
}) { |
||||
await this.rootPage |
||||
.locator(`.ant-picker-time-panel-column:nth-child(1) > .ant-picker-time-panel-cell:nth-child(${hour + 1})`) |
||||
.click(); |
||||
await this.rootPage |
||||
.locator(`.ant-picker-time-panel-column:nth-child(2) > .ant-picker-time-panel-cell:nth-child(${minute + 1})`) |
||||
.click(); |
||||
if (second != null) { |
||||
await this.rootPage |
||||
.locator(`.ant-picker-time-panel-column:nth-child(3) > .ant-picker-time-panel-cell:nth-child(${second + 1})`) |
||||
.click(); |
||||
} |
||||
} |
||||
|
||||
async close() { |
||||
await this.rootPage.keyboard.press('Escape'); |
||||
} |
||||
} |
@ -0,0 +1,113 @@
|
||||
import { test } from '@playwright/test'; |
||||
import { DashboardPage } from '../pages/Dashboard'; |
||||
import setup from '../setup'; |
||||
|
||||
const dateTimeData = [ |
||||
{ |
||||
dateFormat: 'YYYY-MM-DD', |
||||
timeFormat: 'HH:mm', |
||||
date: '2022-12-12', |
||||
hour: 10, |
||||
minute: 20, |
||||
output: '2022-12-12 10:20', |
||||
}, |
||||
{ |
||||
dateFormat: 'YYYY-MM-DD', |
||||
timeFormat: 'HH:mm:ss', |
||||
date: '2022-12-11', |
||||
hour: 20, |
||||
minute: 30, |
||||
second: 40, |
||||
output: '2022-12-11 20:30:40', |
||||
}, |
||||
{ |
||||
dateFormat: 'YYYY/MM/DD', |
||||
timeFormat: 'HH:mm', |
||||
date: '2022-12-13', |
||||
hour: 10, |
||||
minute: 20, |
||||
output: '2022/12/13 10:20', |
||||
}, |
||||
{ |
||||
dateFormat: 'YYYY/MM/DD', |
||||
timeFormat: 'HH:mm:ss', |
||||
date: '2022-12-14', |
||||
hour: 5, |
||||
minute: 30, |
||||
second: 40, |
||||
output: '2022/12/14 05:30:40', |
||||
}, |
||||
{ |
||||
dateFormat: 'DD-MM-YYYY', |
||||
timeFormat: 'HH:mm', |
||||
date: '2022-12-10', |
||||
hour: 4, |
||||
minute: 30, |
||||
output: '10-12-2022 04:30', |
||||
}, |
||||
{ |
||||
dateFormat: 'DD-MM-YYYY', |
||||
timeFormat: 'HH:mm:ss', |
||||
date: '2022-12-26', |
||||
hour: 2, |
||||
minute: 30, |
||||
second: 40, |
||||
output: '26-12-2022 02:30:40', |
||||
}, |
||||
]; |
||||
|
||||
test.describe('DateTime Column', () => { |
||||
let dashboard: DashboardPage; |
||||
let context: any; |
||||
|
||||
test.beforeEach(async ({ page }) => { |
||||
context = await setup({ page }); |
||||
dashboard = new DashboardPage(page, context.project); |
||||
}); |
||||
|
||||
test('Create DateTime Column', async () => { |
||||
await dashboard.treeView.createTable({ title: 'test_datetime' }); |
||||
// Create DateTime column
|
||||
await dashboard.grid.column.create({ |
||||
title: 'NC_DATETIME_0', |
||||
type: 'DateTime', |
||||
dateFormat: dateTimeData[0].dateFormat, |
||||
timeFormat: dateTimeData[0].timeFormat, |
||||
}); |
||||
|
||||
for (let i = 0; i < dateTimeData.length; i++) { |
||||
// Edit DateTime column
|
||||
await dashboard.grid.column.openEdit({ |
||||
title: 'NC_DATETIME_0', |
||||
type: 'DateTime', |
||||
dateFormat: dateTimeData[i].dateFormat, |
||||
timeFormat: dateTimeData[i].timeFormat, |
||||
}); |
||||
|
||||
await dashboard.grid.column.save({ isUpdated: true }); |
||||
|
||||
await dashboard.grid.cell.dateTime.open({ |
||||
index: 0, |
||||
columnHeader: 'NC_DATETIME_0', |
||||
}); |
||||
|
||||
await dashboard.grid.cell.dateTime.selectDate({ |
||||
date: dateTimeData[i].date, |
||||
}); |
||||
|
||||
await dashboard.grid.cell.dateTime.selectTime({ |
||||
hour: dateTimeData[i].hour, |
||||
minute: dateTimeData[i].minute, |
||||
second: dateTimeData[i].second, |
||||
}); |
||||
|
||||
await dashboard.grid.cell.dateTime.save(); |
||||
|
||||
await dashboard.grid.cell.verifyDateCell({ |
||||
index: 0, |
||||
columnHeader: 'NC_DATETIME_0', |
||||
value: dateTimeData[i].output, |
||||
}); |
||||
} |
||||
}); |
||||
}); |
Loading…
Reference in new issue