@nauticana/sail 1.0.7 → 1.0.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nauticana/sail",
3
- "version": "1.0.7",
3
+ "version": "1.0.8",
4
4
  "description": "Metadata-driven Angular component library for CRUD admin frontends backed by the keel Go backend. Tables, forms, navigation, OTP/2FA/social auth, billing, payout onboarding.",
5
5
  "main": "src/index.ts",
6
6
  "scripts": {
@@ -267,12 +267,26 @@ export abstract class BaseTable {
267
267
  return String(rawValue);
268
268
  }
269
269
 
270
- isTimeStamp(fieldName: string): boolean {
270
+ /**
271
+ * Temporal input kind for a field, or null if not temporal. Drives both the
272
+ * load-format and save-restore so TIME/DATE columns aren't given the
273
+ * datetime-local (seconds + 'Z') treatment a Postgres TIME/DATE rejects.
274
+ */
275
+ temporalKind(fieldName: string): 'time' | 'date' | 'datetime-local' | null {
271
276
  const col = this.getColumn(fieldName)
272
- if (col) {
273
- return col.DataType === 'timestamp';
277
+ if (col?.InputType === 'time') return 'time'
278
+ if (col?.InputType === 'date') return 'date'
279
+ if (col?.InputType === 'datetime-local') return 'datetime-local'
280
+ // Fallback for column metadata predating keel's time/date split.
281
+ if (col ? col.DataType === 'timestamp'
282
+ : (fieldName.endsWith('Time') || fieldName.endsWith('time'))) {
283
+ return 'datetime-local'
274
284
  }
275
- return fieldName.endsWith('Time') || fieldName.endsWith('time');
285
+ return null
286
+ }
287
+
288
+ isTimeStamp(fieldName: string): boolean {
289
+ return this.temporalKind(fieldName) !== null
276
290
  }
277
291
 
278
292
  isNumber(fieldName: string): boolean {
@@ -303,9 +317,11 @@ export abstract class BaseTable {
303
317
  return Array.isArray(value);
304
318
  }
305
319
 
306
- formatTimeStamp(val: string): string {
320
+ formatTimeStamp(val: string, kind: 'time' | 'date' | 'datetime-local' = 'datetime-local'): string {
307
321
  if (val && typeof val === 'string') {
308
- return val.slice(0, 16);
322
+ if (kind === 'time') return val.slice(0, 5); // HH:MM
323
+ if (kind === 'date') return val.slice(0, 10); // YYYY-MM-DD
324
+ return val.slice(0, 16); // YYYY-MM-DDTHH:MM
309
325
  }
310
326
  return val;
311
327
  }
@@ -313,16 +329,20 @@ export abstract class BaseTable {
313
329
  formatRecordTimeStamp(record: any) {
314
330
  if (record) {
315
331
  for (const key of Object.keys(record)) {
316
- if (this.isTimeStamp(key)) {
317
- record[key] = this.formatTimeStamp(record[key]);
332
+ const kind = this.temporalKind(key);
333
+ if (kind) {
334
+ record[key] = this.formatTimeStamp(record[key], kind);
318
335
  }
319
336
  }
320
337
  }
321
338
  }
322
339
 
323
- restoreTimeStamp(val: any): any {
324
- // PostgreSQL can't cast '' to TIMESTAMP — normalize empty to null.
340
+ restoreTimeStamp(val: any, kind: 'time' | 'date' | 'datetime-local' = 'datetime-local'): any {
341
+ // PostgreSQL can't cast '' to a temporal type — normalize empty to null.
325
342
  if (val === '' || val === undefined || val === null) return null;
343
+ // TIME/DATE values are sent verbatim ('08:45', '2026-06-15'); only
344
+ // datetime-local needs the seconds + UTC marker the picker omits.
345
+ if (kind === 'time' || kind === 'date') return val;
326
346
  if (typeof val === 'string' && !val.endsWith('Z')) {
327
347
  return val + ':00Z';
328
348
  }
@@ -332,8 +352,9 @@ export abstract class BaseTable {
332
352
  restoreRecordTimeStamp(record: any) {
333
353
  if (record) {
334
354
  for (const key of Object.keys(record)) {
335
- if (this.isTimeStamp(key)) {
336
- record[key] = this.restoreTimeStamp(record[key]);
355
+ const kind = this.temporalKind(key);
356
+ if (kind) {
357
+ record[key] = this.restoreTimeStamp(record[key], kind);
337
358
  }
338
359
  }
339
360
  }
@@ -72,6 +72,7 @@ export class DynamicField extends BaseTable {
72
72
  get isDateLike(): boolean {
73
73
  if (!this.col) return false;
74
74
  return this.col.InputType === 'date'
75
+ || this.col.InputType === 'time'
75
76
  || this.col.InputType === 'datetime'
76
77
  || this.col.InputType === 'datetime-local';
77
78
  }
@@ -40,7 +40,7 @@ export interface TableColumn {
40
40
  PascalName: string;
41
41
  Caption: string;
42
42
  DataType: string;
43
- InputType: 'text' | 'password' | 'number' | 'date' | 'datetime' | 'datetime-local' | 'textarea' | 'select' | 'checkbox';
43
+ InputType: 'text' | 'password' | 'number' | 'date' | 'time' | 'datetime' | 'datetime-local' | 'textarea' | 'select' | 'checkbox';
44
44
  Size: number;
45
45
  Scale: number;
46
46
  Step: string;