From f48225159bc534bb0fd51d1c4de0e3199589a29e Mon Sep 17 00:00:00 2001 From: Dominik Pschenitschni Date: Thu, 11 Aug 2022 23:42:45 +0200 Subject: [PATCH] feat: add zod schemas (cherry picked from commit https://kolaente.dev/vikunja/frontend/pulls/2225) --- frontend/package.json | 5 +- frontend/pnpm-lock.yaml | 22 ++++ frontend/src/modelSchema/LabelTask.ts | 13 ++ frontend/src/modelSchema/abstract.ts | 10 ++ frontend/src/modelSchema/attachment.ts | 19 +++ frontend/src/modelSchema/avatar.ts | 18 +++ frontend/src/modelSchema/backgroundImage.ts | 19 +++ frontend/src/modelSchema/bucket.ts | 26 ++++ frontend/src/modelSchema/caldavToken.ts | 14 +++ .../src/modelSchema/common/RelationKind.ts | 22 ++++ frontend/src/modelSchema/common/date.ts | 11 ++ frontend/src/modelSchema/common/filter.ts | 80 ++++++++++++ frontend/src/modelSchema/common/hexColor.ts | 10 ++ frontend/src/modelSchema/common/id.ts | 6 + frontend/src/modelSchema/common/repeats.ts | 32 ++--- frontend/src/modelSchema/common/textField.ts | 3 + frontend/src/modelSchema/emailUpdate.ts | 11 ++ frontend/src/modelSchema/file.ts | 15 +++ frontend/src/modelSchema/label.ts | 33 +++++ frontend/src/modelSchema/linkShare.ts | 26 ++++ frontend/src/modelSchema/list.ts | 32 +++++ frontend/src/modelSchema/listDuplication.ts | 14 +++ frontend/src/modelSchema/namespace.ts | 26 ++++ frontend/src/modelSchema/notification.ts | 54 ++++++++ frontend/src/modelSchema/passwordReset.ts | 13 ++ frontend/src/modelSchema/passwordUpdate.ts | 15 +++ frontend/src/modelSchema/savedFilter.ts | 23 ++++ frontend/src/modelSchema/subscription.ts | 19 +++ frontend/src/modelSchema/task.ts | 119 ++++++++++++++++++ frontend/src/modelSchema/taskAssignee.ts | 14 +++ frontend/src/modelSchema/taskComment.ts | 20 +++ frontend/src/modelSchema/taskRelation.ts | 24 ++++ frontend/src/modelSchema/team.ts | 25 ++++ frontend/src/modelSchema/teamList.ts | 11 ++ frontend/src/modelSchema/teamMember.ts | 13 ++ frontend/src/modelSchema/teamNamespace.ts | 11 ++ frontend/src/modelSchema/teamShareBase.ts | 19 +++ frontend/src/modelSchema/token.ts | 9 ++ frontend/src/modelSchema/totp.ts | 12 ++ frontend/src/modelSchema/user.ts | 21 ++++ frontend/src/modelSchema/userList.ts | 11 ++ frontend/src/modelSchema/userNamespace.ts | 11 ++ frontend/src/modelSchema/userSettings.ts | 29 +++++ frontend/src/modelSchema/userShareBase.ts | 19 +++ frontend/src/modelTypes/IAvatar.ts | 9 +- 45 files changed, 952 insertions(+), 16 deletions(-) create mode 100644 frontend/src/modelSchema/LabelTask.ts create mode 100644 frontend/src/modelSchema/abstract.ts create mode 100644 frontend/src/modelSchema/attachment.ts create mode 100644 frontend/src/modelSchema/avatar.ts create mode 100644 frontend/src/modelSchema/backgroundImage.ts create mode 100644 frontend/src/modelSchema/bucket.ts create mode 100644 frontend/src/modelSchema/caldavToken.ts create mode 100644 frontend/src/modelSchema/common/RelationKind.ts create mode 100644 frontend/src/modelSchema/common/date.ts create mode 100644 frontend/src/modelSchema/common/filter.ts create mode 100644 frontend/src/modelSchema/common/hexColor.ts create mode 100644 frontend/src/modelSchema/common/id.ts create mode 100644 frontend/src/modelSchema/common/textField.ts create mode 100644 frontend/src/modelSchema/emailUpdate.ts create mode 100644 frontend/src/modelSchema/file.ts create mode 100644 frontend/src/modelSchema/label.ts create mode 100644 frontend/src/modelSchema/linkShare.ts create mode 100644 frontend/src/modelSchema/list.ts create mode 100644 frontend/src/modelSchema/listDuplication.ts create mode 100644 frontend/src/modelSchema/namespace.ts create mode 100644 frontend/src/modelSchema/notification.ts create mode 100644 frontend/src/modelSchema/passwordReset.ts create mode 100644 frontend/src/modelSchema/passwordUpdate.ts create mode 100644 frontend/src/modelSchema/savedFilter.ts create mode 100644 frontend/src/modelSchema/subscription.ts create mode 100644 frontend/src/modelSchema/task.ts create mode 100644 frontend/src/modelSchema/taskAssignee.ts create mode 100644 frontend/src/modelSchema/taskComment.ts create mode 100644 frontend/src/modelSchema/taskRelation.ts create mode 100644 frontend/src/modelSchema/team.ts create mode 100644 frontend/src/modelSchema/teamList.ts create mode 100644 frontend/src/modelSchema/teamMember.ts create mode 100644 frontend/src/modelSchema/teamNamespace.ts create mode 100644 frontend/src/modelSchema/teamShareBase.ts create mode 100644 frontend/src/modelSchema/token.ts create mode 100644 frontend/src/modelSchema/totp.ts create mode 100644 frontend/src/modelSchema/user.ts create mode 100644 frontend/src/modelSchema/userList.ts create mode 100644 frontend/src/modelSchema/userNamespace.ts create mode 100644 frontend/src/modelSchema/userSettings.ts create mode 100644 frontend/src/modelSchema/userShareBase.ts diff --git a/frontend/package.json b/frontend/package.json index 21cb98b6a..ed946ae49 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -115,13 +115,15 @@ "sortablejs": "1.15.2", "tippy.js": "6.3.7", "ufo": "1.4.0", + "validator": "13.9.0", "vue": "3.4.15", "vue-advanced-cropper": "2.8.8", "vue-flatpickr-component": "11.0.3", "vue-i18n": "9.9.1", "vue-router": "4.2.5", "workbox-precaching": "7.0.0", - "zhyswan-vuedraggable": "4.1.3" + "zhyswan-vuedraggable": "4.1.3", + "zod": "3.20.6" }, "devDependencies": { "@4tw/cypress-drag-drop": "2.2.5", @@ -141,6 +143,7 @@ "@types/node": "20.11.10", "@types/postcss-preset-env": "7.7.0", "@types/sortablejs": "1.15.7", + "@types/validator": "13.7.12", "@typescript-eslint/eslint-plugin": "6.20.0", "@typescript-eslint/parser": "6.20.0", "@vitejs/plugin-legacy": "5.3.0", diff --git a/frontend/pnpm-lock.yaml b/frontend/pnpm-lock.yaml index c98ad6449..34c237435 100644 --- a/frontend/pnpm-lock.yaml +++ b/frontend/pnpm-lock.yaml @@ -214,6 +214,9 @@ dependencies: ufo: specifier: 1.4.0 version: 1.4.0 + validator: + specifier: 13.9.0 + version: 13.9.0 vue: specifier: 3.4.15 version: 3.4.15(typescript@5.3.3) @@ -235,6 +238,9 @@ dependencies: zhyswan-vuedraggable: specifier: 4.1.3 version: 4.1.3(vue@3.4.15) + zod: + specifier: 3.20.6 + version: 3.20.6 devDependencies: '@4tw/cypress-drag-drop': @@ -285,6 +291,9 @@ devDependencies: '@types/sortablejs': specifier: 1.15.7 version: 1.15.7 + '@types/validator': + specifier: 13.7.12 + version: 13.7.12 '@typescript-eslint/eslint-plugin': specifier: 6.20.0 version: 6.20.0(@typescript-eslint/parser@6.20.0)(eslint@8.56.0)(typescript@5.3.3) @@ -4918,6 +4927,10 @@ packages: resolution: {integrity: sha512-zC0iXxAv1C1ERURduJueYzkzZ2zaGyc+P2c95hgkikHPr3z8EdUZOlgEQ5X0DRmwDZn+hekycQnoeiiRVrmilQ==} dev: false + /@types/validator@13.7.12: + resolution: {integrity: sha512-YVtyAPqpefU+Mm/qqnOANW6IkqKpCSrarcyV269C8MA8Ux0dbkEuQwM/4CjL47kVEM2LgBef/ETfkH+c6+moFA==} + dev: true + /@types/web-bluetooth@0.0.16: resolution: {integrity: sha512-oh8q2Zc32S6gd/j50GowEjKLoOVOwHP/bWVjKJInBwQqdOYMdPrf1oVlelTlyfFK3CKxL1uahMDAr+vy8T7yMQ==} dev: false @@ -11000,6 +11013,11 @@ packages: spdx-expression-parse: 3.0.1 dev: true + /validator@13.9.0: + resolution: {integrity: sha512-B+dGG8U3fdtM0/aNK4/X8CXq/EcxU2WPrPEkJGslb47qyHsxmbggTWK0yEA4qnYVNF+nxNlN88o14hIcPmSIEA==} + engines: {node: '>= 0.10'} + dev: false + /verror@1.10.0: resolution: {integrity: sha512-ZZKSmDAEFOijERBLkmYfJ+vmk3w+7hOLYDNkRCuRuMJGEmqYNCNLyBBFwWKVMhfwaEF3WOd0Zlw86U/WC/+nYw==} engines: {'0': node >=0.6.0} @@ -11784,6 +11802,10 @@ packages: vue: 3.4.15(typescript@5.3.3) dev: false + /zod@3.20.6: + resolution: {integrity: sha512-oyu0m54SGCtzh6EClBVqDDlAYRz4jrVtKwQ7ZnsEmMI9HnzuZFj8QFwAY1M5uniIYACdGvv0PBWPF2kO0aNofA==} + dev: false + /zod@3.22.4: resolution: {integrity: sha512-iC+8Io04lddc+mVqQ9AZ7OQ2MrUKGN+oIQyq1vemgt46jwCwLfhq7/pwnBnNXXXZb8VTVLKwp9EDkx+ryxIWmg==} dev: true diff --git a/frontend/src/modelSchema/LabelTask.ts b/frontend/src/modelSchema/LabelTask.ts new file mode 100644 index 000000000..2f5bbe3f5 --- /dev/null +++ b/frontend/src/modelSchema/LabelTask.ts @@ -0,0 +1,13 @@ +import type {TypeOf} from 'zod' + +import {IdSchema} from './common/id' + +import {AbstractSchema} from './abstract' + +export const LabelTaskSchema = AbstractSchema.extend({ + id: IdSchema.nullable(), + taskId: IdSchema.nullable(), + labelId: IdSchema.nullable(), +}) + +export type LabelTask = TypeOf \ No newline at end of file diff --git a/frontend/src/modelSchema/abstract.ts b/frontend/src/modelSchema/abstract.ts new file mode 100644 index 000000000..e433a0147 --- /dev/null +++ b/frontend/src/modelSchema/abstract.ts @@ -0,0 +1,10 @@ +import type {TypeOf} from 'zod' +import {object, nativeEnum} from 'zod' + +import {RIGHTS} from '@/constants/rights' + +export const AbstractSchema = object({ + maxRight: nativeEnum(RIGHTS).nullable(), +}) + +export type IAbstract = TypeOf \ No newline at end of file diff --git a/frontend/src/modelSchema/attachment.ts b/frontend/src/modelSchema/attachment.ts new file mode 100644 index 000000000..e39ee4e02 --- /dev/null +++ b/frontend/src/modelSchema/attachment.ts @@ -0,0 +1,19 @@ +import type {TypeOf} from 'zod' + +import {IdSchema} from './common/id' +import {DateSchema} from './common/date' + +import {AbstractSchema} from './abstract' +import {UserSchema} from './user' +import {FileSchema} from './file' + +export const AttachmentSchema = AbstractSchema.extend({ + id: IdSchema.default(0), + + taskId: IdSchema.default(0), // iTaskSchema.shape.id + createdBy: UserSchema, + file: FileSchema, + created: DateSchema.nullable(), +}) + +export type IAttachment = TypeOf \ No newline at end of file diff --git a/frontend/src/modelSchema/avatar.ts b/frontend/src/modelSchema/avatar.ts new file mode 100644 index 000000000..551c31d05 --- /dev/null +++ b/frontend/src/modelSchema/avatar.ts @@ -0,0 +1,18 @@ +import type {TypeOf} from 'zod' +import {z, string, object} from 'zod' + +export const AVATAR_PROVIDER = [ + 'default', + 'initials', + 'gravatar', + 'marble', + 'upload', +] as const +export const AvatarProviderSchema = z.enum(AVATAR_PROVIDER) +export type IAvatarProvider = TypeOf + +export const AvatarSchema = object({ + // FIXME: shouldn't the default be 'default'? + avatarProvider: string().or(AvatarProviderSchema).default(''), +}) +export type IAvatar = TypeOf \ No newline at end of file diff --git a/frontend/src/modelSchema/backgroundImage.ts b/frontend/src/modelSchema/backgroundImage.ts new file mode 100644 index 000000000..51b147157 --- /dev/null +++ b/frontend/src/modelSchema/backgroundImage.ts @@ -0,0 +1,19 @@ +import type {TypeOf} from 'zod' +import {object, record, string, unknown} from 'zod' + +import {IdSchema} from './common/id' + +export const BackgroundImageSchema = object({ + id: IdSchema.default(0), + url: string().url().default(''), + thumb: string().default(''), + // FIXME: not sure if this needs to defined, since it seems provider specific + // { + // author: string(), + // authorName: string(), + // } + info: record(unknown()).default({}), + blurHash: string().default(''), +}) + +export type BackgroundImage = TypeOf \ No newline at end of file diff --git a/frontend/src/modelSchema/bucket.ts b/frontend/src/modelSchema/bucket.ts new file mode 100644 index 000000000..c3df6ea0b --- /dev/null +++ b/frontend/src/modelSchema/bucket.ts @@ -0,0 +1,26 @@ +import type {TypeOf} from 'zod' +import {number, array, boolean} from 'zod' + +import {IdSchema} from './common/id' +import {DateSchema} from './common/date' +import {TextFieldSchema} from './common/textField' + +import {AbstractSchema} from './abstract' +import {UserSchema} from './user' +import {TaskSchema} from './task' + +export const BucketSchema = AbstractSchema.extend({ + id: IdSchema.default(0), + title: TextFieldSchema, + listId: IdSchema.default(0), + limit: number().default(0), + tasks: array(TaskSchema).default([]), + isDoneBucket: boolean().default(false), + position: number().default(0), + + createdBy: UserSchema.nullable(), + created: DateSchema.nullable(), + updated: DateSchema.nullable(), +}) + +export type IBucket = TypeOf \ No newline at end of file diff --git a/frontend/src/modelSchema/caldavToken.ts b/frontend/src/modelSchema/caldavToken.ts new file mode 100644 index 000000000..6eeb5019f --- /dev/null +++ b/frontend/src/modelSchema/caldavToken.ts @@ -0,0 +1,14 @@ + +import type {TypeOf} from 'zod' + +import {IdSchema} from './common/id' +import {DateSchema} from './common/date' + +import {AbstractSchema} from './abstract' + +export const CaldavTokenSchema = AbstractSchema.extend({ + id: IdSchema, + created: DateSchema, +}) + +export type CaldavToken = TypeOf diff --git a/frontend/src/modelSchema/common/RelationKind.ts b/frontend/src/modelSchema/common/RelationKind.ts new file mode 100644 index 000000000..bf3bc55d8 --- /dev/null +++ b/frontend/src/modelSchema/common/RelationKind.ts @@ -0,0 +1,22 @@ +import type {TypeOf} from 'zod' +import {nativeEnum} from 'zod' + +export const RELATION_KIND = { + 'SUBTASK': 'subtask', + 'PARENTTASK': 'parenttask', + 'RELATED': 'related', + 'DUPLICATES': 'duplicates', + 'BLOCKING': 'blocking', + 'BLOCKED': 'blocked', + 'PROCEDES': 'precedes', + 'FOLLOWS': 'follows', + 'COPIEDFROM': 'copiedfrom', + 'COPIEDTO': 'copiedto', +} as const + +export const RELATION_KINDS = [...Object.values(RELATION_KIND)] as const + +export const RelationKindSchema = nativeEnum(RELATION_KIND) + +export type IRelationKind = TypeOf + \ No newline at end of file diff --git a/frontend/src/modelSchema/common/date.ts b/frontend/src/modelSchema/common/date.ts new file mode 100644 index 000000000..5478c9134 --- /dev/null +++ b/frontend/src/modelSchema/common/date.ts @@ -0,0 +1,11 @@ +import {preprocess, date} from 'zod' + +export const DateSchema = preprocess((arg) => { + if ( + // FIXME: Add comment why we check for `0001` + typeof arg == 'string' && !arg.startsWith('0001') || + arg instanceof Date + ) { + return new Date(arg) + } +}, date()) \ No newline at end of file diff --git a/frontend/src/modelSchema/common/filter.ts b/frontend/src/modelSchema/common/filter.ts new file mode 100644 index 000000000..2dbdebc49 --- /dev/null +++ b/frontend/src/modelSchema/common/filter.ts @@ -0,0 +1,80 @@ +import type {TypeOf} from 'zod' +import {z, nativeEnum, array, boolean, object, number} from 'zod' + +export enum SORT_BY { + ID = 'id', + DONE = 'done', + TITLE = 'title', + PRIORITY = 'priority', + DONE_AT = 'done_at', + DUE_DATE = 'due_date', + START_DATE = 'start_date', + END_DATE = 'end_date', + PERCENT_DONE = 'percent_done', + CREATED = 'created', + UPDATED = 'updated', + POSITION = 'position', + KANBAN_POSITION = 'kanban_position', + } + +export enum ORDER_BY { + ASC = 'asc', + DESC = 'desc', + NONE = 'none', +} + +export enum FILTER_BY { + DONE = 'done', + DUE_DATE = 'due_date', + START_DATE = 'start_date', + END_DATE = 'end_date', + NAMESPACE = 'namespace', + ASSIGNEES = 'assignees', + LIST_ID = 'list_id', + BUCKET_ID = 'bucket_id', + PRIORITY = 'priority', + PERCENT_DONE = 'percent_done', + LABELS = 'labels', + UNDEFINED = 'undefined', // FIXME: Why do we have a value that is undefined as string? +} + +export enum FILTER_COMPARATOR { + EQUALS = 'equals', + LESS = 'less', + GREATER = 'greater', + GREATER_EQUALS = 'greater_equals', + LESS_EQUALS = 'less_equals', + IN = 'in', +} + +export enum FILTER_CONCAT { + AND = 'and', + OR = 'or', + IN = 'in', +} + +const TASKS_PER_BUCKET = 25 + +export const FilterSchema = object({ + sortBy: array(nativeEnum(SORT_BY)).default([SORT_BY.DONE, SORT_BY.ID]), // FIXME: create from taskSchema, + // fixme default order seem so also be `desc` + // see line from ListTable: + // if (typeof order === 'undefined' || order === 'none') { + orderBy: array(nativeEnum(ORDER_BY)).default([ORDER_BY.ASC, ORDER_BY.DESC]), + // FIXME: create from taskSchema + filterBy: array(nativeEnum(FILTER_BY)).default([FILTER_BY.DONE]), + // FIXME: create from taskSchema + // FIXME: might need to preprocess values, e.g. date. + // see line from 'filters.vue': + // params.filter_value = params.filter_value.map(v => v instanceof Date ? v.toISOString() : v) + filterValue: array(z.enum(['false'])).default(['false']), + // FIXME: is `in` value correct? + // found in `quick-actions.vue`: + // params.filter_comparator.push('in') + filterComparator: array(nativeEnum(FILTER_COMPARATOR)).default([FILTER_COMPARATOR.EQUALS]), + filterConcat: z.nativeEnum(FILTER_CONCAT).default(FILTER_CONCAT.AND), + filterIncludeNulls: boolean().default(true), + perPage: number().default(TASKS_PER_BUCKET), // FIXME: is perPage is just available for the bucket endpoint? +}) + +export type IFilter = TypeOf \ No newline at end of file diff --git a/frontend/src/modelSchema/common/hexColor.ts b/frontend/src/modelSchema/common/hexColor.ts new file mode 100644 index 000000000..53f87b7ff --- /dev/null +++ b/frontend/src/modelSchema/common/hexColor.ts @@ -0,0 +1,10 @@ +import {string} from 'zod' +import isHexColor from 'validator/lib/isHexColor' + +export const HexColorSchema = string().transform( + (value) => { + if (!value || value.startsWith('#')) { + return value + } + return '#' + value + }).refine(value => isHexColor(value)) \ No newline at end of file diff --git a/frontend/src/modelSchema/common/id.ts b/frontend/src/modelSchema/common/id.ts new file mode 100644 index 000000000..f4181ee71 --- /dev/null +++ b/frontend/src/modelSchema/common/id.ts @@ -0,0 +1,6 @@ +import {number, preprocess} from 'zod' + +export const IdSchema = preprocess( + (value: unknown) => Number(value), + number().positive().int(), +) \ No newline at end of file diff --git a/frontend/src/modelSchema/common/repeats.ts b/frontend/src/modelSchema/common/repeats.ts index da89e1f95..e9125118e 100644 --- a/frontend/src/modelSchema/common/repeats.ts +++ b/frontend/src/modelSchema/common/repeats.ts @@ -1,24 +1,26 @@ +import type {TypeOf} from 'zod' +import {nativeEnum, number, object, preprocess} from 'zod' + import {SECONDS_A_HOUR} from '@/constants/date' -import { REPEAT_TYPES, type IRepeatAfter } from '@/types/IRepeatAfter' -import { nativeEnum, number, object, preprocess } from 'zod' +import {REPEAT_TYPES, type IRepeatAfter} from '@/types/IRepeatAfter' /** * Parses `repeatAfterSeconds` into a usable js object. */ export function parseRepeatAfter(repeatAfterSeconds: number): IRepeatAfter { - let repeatAfter: IRepeatAfter = {type: 'hours', amount: repeatAfterSeconds / SECONDS_A_HOUR} + let repeatAfter: IRepeatAfter - // if its dividable by 24, its something with days, otherwise hours + // if its dividable by SECONDS_A_DAY, its something with days, otherwise hours if (repeatAfterSeconds % SECONDS_A_DAY === 0) { - if (repeatAfterSeconds % SECONDS_A_WEEK === 0) { - repeatAfter = {type: 'weeks', amount: repeatAfterSeconds / SECONDS_A_WEEK} - } else if (repeatAfterSeconds % SECONDS_A_MONTH === 0) { - repeatAfter = {type:'months', amount: repeatAfterSeconds / SECONDS_A_MONTH} - } else if (repeatAfterSeconds % SECONDS_A_YEAR === 0) { - repeatAfter = {type: 'years', amount: repeatAfterSeconds / SECONDS_A_YEAR} - } else { - repeatAfter = {type: 'days', amount: repeatAfterSeconds / SECONDS_A_DAY} - } + repeatAfter = {type: REPEAT_TYPES.HOURS, amount: repeatAfterSeconds / SECONDS_A_HOUR} + } else if (repeatAfterSeconds % SECONDS_A_WEEK === 0) { + repeatAfter = {type: REPEAT_TYPES.WEEKS, amount: repeatAfterSeconds / SECONDS_A_WEEK} + } else if (repeatAfterSeconds % SECONDS_A_MONTH === 0) { + repeatAfter = {type: REPEAT_TYPES.MONTHS, amount: repeatAfterSeconds / SECONDS_A_MONTH} + } else if (repeatAfterSeconds % SECONDS_A_YEAR === 0) { + repeatAfter = {type: REPEAT_TYPES.YEARS, amount: repeatAfterSeconds / SECONDS_A_YEAR} + } else { + repeatAfter = {type: REPEAT_TYPES.DAYS, amount: repeatAfterSeconds / SECONDS_A_DAY} } return repeatAfter } @@ -37,4 +39,6 @@ export const RepeatsSchema = preprocess( type: nativeEnum(REPEAT_TYPES), amount: number().int(), }), -) \ No newline at end of file +) + +export type RepeatAfter = TypeOf \ No newline at end of file diff --git a/frontend/src/modelSchema/common/textField.ts b/frontend/src/modelSchema/common/textField.ts new file mode 100644 index 000000000..bda96f493 --- /dev/null +++ b/frontend/src/modelSchema/common/textField.ts @@ -0,0 +1,3 @@ +import {string} from 'zod' + +export const TextFieldSchema = string().transform((value) => value.trim()).default('') \ No newline at end of file diff --git a/frontend/src/modelSchema/emailUpdate.ts b/frontend/src/modelSchema/emailUpdate.ts new file mode 100644 index 000000000..79214a029 --- /dev/null +++ b/frontend/src/modelSchema/emailUpdate.ts @@ -0,0 +1,11 @@ +import type {TypeOf} from 'zod' +import {string} from 'zod' + +import {AbstractSchema} from './abstract' + +export const EmailUpdateSchema = AbstractSchema.extend({ + newEmail: string().email().default(''), + password: string().default(''), +}) + +export type EmailUpdate = TypeOf diff --git a/frontend/src/modelSchema/file.ts b/frontend/src/modelSchema/file.ts new file mode 100644 index 000000000..03918a691 --- /dev/null +++ b/frontend/src/modelSchema/file.ts @@ -0,0 +1,15 @@ +import type {TypeOf} from 'zod' +import {object, number, string} from 'zod' + +import {IdSchema} from './common/id' +import {DateSchema} from './common/date' + +export const FileSchema = object({ + id: IdSchema.default(0), + mime: string().default(''), + name: string().default(''), + size: number().default(0), + created: DateSchema.nullable(), +}) + +export type File = TypeOf diff --git a/frontend/src/modelSchema/label.ts b/frontend/src/modelSchema/label.ts new file mode 100644 index 000000000..9ba2f0f6b --- /dev/null +++ b/frontend/src/modelSchema/label.ts @@ -0,0 +1,33 @@ +import type {TypeOf} from 'zod' +import {string} from 'zod' + +import {IdSchema} from './common/id' +import {DateSchema} from './common/date' +import {HexColorSchema} from './common/hexColor' + +import {UserSchema} from './user' +import {AbstractSchema} from './abstract' + +import {colorIsDark} from '@/helpers/color/colorIsDark' + +const DEFAULT_LABEL_BACKGROUND_COLOR = 'e8e8e8' + +export const LabelSchema = AbstractSchema.extend({ + id: IdSchema.default(0), + title: string().default(''), + hexColor: HexColorSchema.default(DEFAULT_LABEL_BACKGROUND_COLOR), + textColor: string(), // implicit + description: string().default(''), + createdBy: UserSchema, // FIXME: default: current user? + listId: IdSchema.default(0), + + created: DateSchema.nullable(), + updated: DateSchema.nullable(), +}).transform((obj) => { + // FIXME: remove textColor location => should be defined in UI + obj.textColor = colorIsDark(obj.hexColor) ? '#4a4a4a' : '#ffffff' + return obj +}, +) + +export type ILabel = TypeOf diff --git a/frontend/src/modelSchema/linkShare.ts b/frontend/src/modelSchema/linkShare.ts new file mode 100644 index 000000000..9d671f58b --- /dev/null +++ b/frontend/src/modelSchema/linkShare.ts @@ -0,0 +1,26 @@ +import type {TypeOf} from 'zod' +import {number, string, nativeEnum} from 'zod' + +import {IdSchema} from './common/id' +import {DateSchema} from './common/date' + +import {AbstractSchema} from './abstract' +import {UserSchema} from './user' + +import {RIGHTS} from '@/constants/rights' + +export const LinkShareSchema = AbstractSchema.extend({ + id: IdSchema.default(0), + hash: string().default(''), + right: nativeEnum(RIGHTS).default(RIGHTS.READ), + sharedBy: UserSchema, + sharingType: number().default(0), // FIXME: use correct numbers + listId: IdSchema.default(0), + name: string().default(''), + password: string().default(''), + + created: DateSchema.nullable(), + updated: DateSchema.nullable(), +}) + +export type LinkShare = TypeOf diff --git a/frontend/src/modelSchema/list.ts b/frontend/src/modelSchema/list.ts new file mode 100644 index 000000000..6a86e9738 --- /dev/null +++ b/frontend/src/modelSchema/list.ts @@ -0,0 +1,32 @@ +import type {TypeOf} from 'zod' +import {boolean, number, string, array, any} from 'zod' + +import {IdSchema} from './common/id' +import {DateSchema} from './common/date' + +import {AbstractSchema} from './abstract' +import {SubscriptionSchema} from './subscription' +import {TaskSchema} from './task' +import {UserSchema} from './user' + +export const ListSchema = AbstractSchema.extend({ + id: IdSchema.default(0), + hash: string().default(''), + description: string().default(''), + owner: UserSchema, + tasks: array(TaskSchema), + namespaceId: IdSchema.default(0), // INamespace['id'], + isArchived: boolean().default(false), + hexColor: string().default(''), + identifier: string().default(''), + backgroundInformation: any().nullable().default(null), // FIXME: what is this for? + isFavorite: boolean().default(false), + subscription: SubscriptionSchema.nullable(), + position: number().default(0), + backgroundBlurHash: string().default(''), + + created: DateSchema.nullable(), + updated: DateSchema.nullable(), +}) + +export type List = TypeOf diff --git a/frontend/src/modelSchema/listDuplication.ts b/frontend/src/modelSchema/listDuplication.ts new file mode 100644 index 000000000..c208e2806 --- /dev/null +++ b/frontend/src/modelSchema/listDuplication.ts @@ -0,0 +1,14 @@ +import type {TypeOf} from 'zod' + +import {IdSchema} from './common/id' + +import {AbstractSchema} from './abstract' +import {ListSchema} from './list' + +export const ListDuplicationSchema = AbstractSchema.extend({ + listId: IdSchema.default(0), + namespaceId: IdSchema.default(0), // INamespace['id'], + list: ListSchema, +}) + +export type ListDuplication = TypeOf \ No newline at end of file diff --git a/frontend/src/modelSchema/namespace.ts b/frontend/src/modelSchema/namespace.ts new file mode 100644 index 000000000..614253691 --- /dev/null +++ b/frontend/src/modelSchema/namespace.ts @@ -0,0 +1,26 @@ +import type {TypeOf} from 'zod' +import {boolean, string, array} from 'zod' + +import {IdSchema} from './common/id' +import {HexColorSchema} from './common/hexColor' + +import {AbstractSchema} from './abstract' +import {ListSchema} from './list' +import {UserSchema} from './user' +import {SubscriptionSchema} from './subscription' + +export const NamespaceSchema = AbstractSchema.extend({ + id: IdSchema.default(0), + title: string().default(''), + description: string().default(''), + owner: UserSchema, + lists: array(ListSchema), + isArchived: boolean().default(false), + hexColor: HexColorSchema.default(''), + subscription: SubscriptionSchema.nullable(), + + created: IdSchema.nullable(), + updated: IdSchema.nullable(), +}) + +export type Namespace = TypeOf diff --git a/frontend/src/modelSchema/notification.ts b/frontend/src/modelSchema/notification.ts new file mode 100644 index 000000000..953bffd61 --- /dev/null +++ b/frontend/src/modelSchema/notification.ts @@ -0,0 +1,54 @@ +import type {TypeOf} from 'zod' +import {union, boolean, object, string} from 'zod' + +import {IdSchema} from './common/id' +import {DateSchema} from './common/date' + +import {AbstractSchema} from './abstract' +import {TaskSchema} from './task' +import {TaskCommentSchema} from './taskComment' +import {TeamSchema} from './team' +import {UserSchema} from './user' + +const NotificationTypeSchema = object({ + doer: UserSchema, +}) + +const NotificationTypeTask = NotificationTypeSchema.extend({ + task: TaskSchema, + comment: TaskCommentSchema, +}) + +const NotificationTypeAssigned = NotificationTypeSchema.extend({ + task: TaskSchema, + assignee: UserSchema, +}) + +const NotificationTypeDeleted = NotificationTypeSchema.extend({ + task: TaskSchema, +}) + +const NotificationTypeCreated = NotificationTypeSchema.extend({ + task: TaskSchema, +}) + +const NotificationTypeMemberAdded = NotificationTypeSchema.extend({ + member: UserSchema, + team: TeamSchema, +}) + +export const NotificationSchema = AbstractSchema.extend({ + id: IdSchema.default(0), + name: string().default(''), + notification: union([ + NotificationTypeTask, + NotificationTypeAssigned, + NotificationTypeDeleted, + NotificationTypeCreated, + NotificationTypeMemberAdded, + ]), + read: boolean().default(false), + readAt: DateSchema.nullable(), +}) + +export type Notification = TypeOf diff --git a/frontend/src/modelSchema/passwordReset.ts b/frontend/src/modelSchema/passwordReset.ts new file mode 100644 index 000000000..b7e12d6ef --- /dev/null +++ b/frontend/src/modelSchema/passwordReset.ts @@ -0,0 +1,13 @@ +import type {TypeOf} from 'zod' +import {string} from 'zod' + +import {AbstractSchema} from './abstract' + +// FIXME: is it correct that this extends the Abstract Schema? +export const PasswordResetSchema = AbstractSchema.extend({ + token: string().default(''), + newPassword: string().default(''), + email: string().email().default(''), +}) + +export type PasswordReset = TypeOf diff --git a/frontend/src/modelSchema/passwordUpdate.ts b/frontend/src/modelSchema/passwordUpdate.ts new file mode 100644 index 000000000..c2063fbcb --- /dev/null +++ b/frontend/src/modelSchema/passwordUpdate.ts @@ -0,0 +1,15 @@ +import type {TypeOf} from 'zod' +import {string} from 'zod' + +import {AbstractSchema} from './abstract' + +// FIXME: is it correct that this extends the Abstract Schema? +export const PasswordUpdateSchema = AbstractSchema.extend({ + newPassword: string().default(''), + oldPassword: string().default(''), +}).refine((data) => data.newPassword === data.oldPassword, { + message: 'Passwords don\'t match', + path: ['confirm'], // path of error +}) + +export type PasswordUpdate = TypeOf diff --git a/frontend/src/modelSchema/savedFilter.ts b/frontend/src/modelSchema/savedFilter.ts new file mode 100644 index 000000000..58215d8fa --- /dev/null +++ b/frontend/src/modelSchema/savedFilter.ts @@ -0,0 +1,23 @@ +import type {TypeOf} from 'zod' +import {string} from 'zod' + +import {IdSchema} from './common/id' +import {DateSchema} from './common/date' +import {FilterSchema} from './common/filter' + +import {AbstractSchema} from './abstract' +import {UserSchema} from './user' + +// FIXME: is it correct that this extends the Abstract Schema? +export const SavedFilterSchema = AbstractSchema.extend({ + id: IdSchema.default(0), + title: string().default(''), + description: string().default(''), + filters: FilterSchema, + + owner: UserSchema, + created: DateSchema.nullable(), + updated: DateSchema.nullable(), +}) + +export type SavedFilter = TypeOf diff --git a/frontend/src/modelSchema/subscription.ts b/frontend/src/modelSchema/subscription.ts new file mode 100644 index 000000000..a550f5d37 --- /dev/null +++ b/frontend/src/modelSchema/subscription.ts @@ -0,0 +1,19 @@ +import type {TypeOf} from 'zod' +import {string} from 'zod' + +import {DateSchema} from './common/date' +import {IdSchema} from './common/id' + +import {AbstractSchema} from './abstract' +import {UserSchema} from './user' + +export const SubscriptionSchema = AbstractSchema.extend({ + id: IdSchema.default(0), + entity: string().default(''), // FIXME: correct type? + entityId: IdSchema.default(0), // FIXME: correct type? + user: UserSchema, + + created: DateSchema.nullable(), +}) + +export type Subscription = TypeOf diff --git a/frontend/src/modelSchema/task.ts b/frontend/src/modelSchema/task.ts new file mode 100644 index 000000000..c6b6585b1 --- /dev/null +++ b/frontend/src/modelSchema/task.ts @@ -0,0 +1,119 @@ +import type {ZodType, TypeOf} from 'zod' +import {nativeEnum, boolean, number, string, array, record, unknown, lazy} from 'zod' + +import {IdSchema} from './common/id' +import {DateSchema} from './common/date' +import {HexColorSchema} from './common/hexColor' +import {TextFieldSchema} from './common/textField' +import {RelationKindSchema} from './common/RelationKind' +import {RepeatsSchema} from './common/repeats' + +import {AbstractSchema} from './abstract' +import {AttachmentSchema} from './attachment' +import {LabelSchema} from './label' +import {SubscriptionSchema} from './subscription' +import {UserSchema} from './user' + +import {PRIORITIES} from '@/constants/priorities' +import {TASK_REPEAT_MODES} from '@/types/IRepeatMode' + +const LabelsSchema = array(LabelSchema) + .transform((labels) => labels.sort((f, s) => f.title > s.title ? 1 : -1)) // FIXME: use + .default([]) + +export type ILabels = TypeOf + +const RelatedTasksSchema = record(RelationKindSchema, record(string(), unknown())) +export type IRelatedTasksSchema = TypeOf + +const RelatedTasksLazySchema : ZodType = lazy(() => + record(RelationKindSchema, TaskSchema), +) +export type IRelatedTasksLazySchema = TypeOf + +// export interface ITask extends IAbstract { +// id: number +// title: string +// description: string +// done: boolean +// doneAt: Date | null +// priority: Priority +// labels: ILabel[] +// assignees: IUser[] + +// dueDate: Date | null +// startDate: Date | null +// endDate: Date | null +// repeatAfter: number | IRepeatAfter +// repeatFromCurrentDate: boolean +// repeatMode: IRepeatMode +// reminderDates: Date[] +// parentTaskId: ITask['id'] +// hexColor: string +// percentDone: number +// relatedTasks: Partial>, +// attachments: IAttachment[] +// identifier: string +// index: number +// isFavorite: boolean +// subscription: ISubscription + +// position: number +// kanbanPosition: number + +// createdBy: IUser +// created: Date +// updated: Date + +// listId: IList['id'] // Meta, only used when creating a new task +// bucketId: IBucket['id'] +// } + +export const TaskSchema = AbstractSchema.extend({ + id: IdSchema.default(0), + title: TextFieldSchema, + description: TextFieldSchema, + done: boolean().default(false), + doneAt: DateSchema.nullable().default(null), + priority: nativeEnum(PRIORITIES).default(PRIORITIES.UNSET), + labels: LabelsSchema, + assignees: array(UserSchema).default([]), + + dueDate: DateSchema.nullable(), // FIXME: default value is `0`. Shouldn't this be `null`? + startDate: DateSchema.nullable(), // FIXME: default value is `0`. Shouldn't this be `null`? + endDate: DateSchema.nullable(), // FIXME: default value is `0`. Shouldn't this be `null`? + repeatAfter: RepeatsSchema, // FIXME: default value is `0`. Shouldn't this be `null`? + repeatFromCurrentDate: boolean().default(false), + repeatMode: nativeEnum(TASK_REPEAT_MODES).default(TASK_REPEAT_MODES.REPEAT_MODE_DEFAULT), + + // TODO: schedule notifications + // FIXME: triggered notificaitons not supported anymore / remove feature? + reminderDates: array(DateSchema).default([]), + parentTaskId: IdSchema.default(0), // shouldn't this have `null` as default? + hexColor: HexColorSchema.default(''), + percentDone: number().default(0), + relatedTasks: RelatedTasksSchema.default({}), + attachments: array(AttachmentSchema).default([]), + identifier: string().default(''), + index: number().default(0), + isFavorite: boolean().default(false), + subscription: SubscriptionSchema.nullable().default(null), + + position: number().default(0), + kanbanPosition: number().default(0), + + createdBy: UserSchema, + created: DateSchema.nullable(), + updated: DateSchema.nullable(), + + listId: IdSchema.default(0), //IList['id'], // Meta, only used when creating a new task + bucketId: IdSchema.default(0), // IBucket['id'], +}).transform((obj) => { + if (obj.identifier === `-${obj.index}`) { + obj.identifier = '' + } + return obj +}) + + +export type Task = TypeOf diff --git a/frontend/src/modelSchema/taskAssignee.ts b/frontend/src/modelSchema/taskAssignee.ts new file mode 100644 index 000000000..54f4e56ab --- /dev/null +++ b/frontend/src/modelSchema/taskAssignee.ts @@ -0,0 +1,14 @@ +import type {TypeOf} from 'zod' + +import {IdSchema} from './common/id' +import {DateSchema} from './common/date' + +import {AbstractSchema} from './abstract' + +export const TaskAssigneeSchema = AbstractSchema.extend({ + created: DateSchema.nullable(), + userId: IdSchema.default(0), // IUser['id'] + taskId: IdSchema.default(0), // ITask['id'] +}) + +export type TaskAssignee = TypeOf diff --git a/frontend/src/modelSchema/taskComment.ts b/frontend/src/modelSchema/taskComment.ts new file mode 100644 index 000000000..1b01264b9 --- /dev/null +++ b/frontend/src/modelSchema/taskComment.ts @@ -0,0 +1,20 @@ +import type {TypeOf} from 'zod' +import {string} from 'zod' + +import {IdSchema} from './common/id' +import {DateSchema} from './common/date' + +import {AbstractSchema} from './abstract' +import {UserSchema} from './user' + +export const TaskCommentSchema = AbstractSchema.extend({ + id: IdSchema.default(0), + taskId: IdSchema.default(0), + comment: string().default(''), + author: UserSchema, + + created: DateSchema.nullable(), + updated: DateSchema.nullable(), +}) + +export type TaskComment = TypeOf diff --git a/frontend/src/modelSchema/taskRelation.ts b/frontend/src/modelSchema/taskRelation.ts new file mode 100644 index 000000000..26b4b845d --- /dev/null +++ b/frontend/src/modelSchema/taskRelation.ts @@ -0,0 +1,24 @@ +import type {TypeOf} from 'zod' +import {nativeEnum} from 'zod' + +import {IdSchema} from './common/id' +import {DateSchema} from './common/date' + +import {AbstractSchema} from './abstract' +import {UserSchema} from './user' + +import {RELATION_KIND} from '@/types/IRelationKind' + +export const TaskRelationSchema = AbstractSchema.extend({ + id: IdSchema.default(0), + otherTaskId: IdSchema.default(0), + taskId: IdSchema.default(0), + relationKind: nativeEnum(RELATION_KIND).nullable().default(null), // FIXME: default value was empty string? + + createdBy: UserSchema, + // FIXME: shouldn't the empty value of dates be `new Date()` + // Because e.g. : `new Date(null)` => Thu Jan 01 1970 01:00:00 GMT+0100 (Central European Standard Time) + created: DateSchema.nullable().default(null), +}) + +export type ITaskRelation = TypeOf diff --git a/frontend/src/modelSchema/team.ts b/frontend/src/modelSchema/team.ts new file mode 100644 index 000000000..df68ecad2 --- /dev/null +++ b/frontend/src/modelSchema/team.ts @@ -0,0 +1,25 @@ +import type {TypeOf} from 'zod' +import {array, nativeEnum, string} from 'zod' + +import {IdSchema} from './common/id' +import {DateSchema} from './common/date' + +import {AbstractSchema} from './abstract' +import {UserSchema} from './user' +import {TeamMemberSchema} from './teamMember' + +import {RIGHTS} from '@/constants/rights' + +export const TeamSchema = AbstractSchema.extend({ + id: IdSchema.default(0), + name: string().default(''), + description: string().default(''), + members: array(TeamMemberSchema), + right: nativeEnum(RIGHTS).default(RIGHTS.READ), + + createdBy: UserSchema, // FIXME: default was {}, + created: DateSchema.nullable(), + updated: DateSchema.nullable(), +}) + +export type Team = TypeOf \ No newline at end of file diff --git a/frontend/src/modelSchema/teamList.ts b/frontend/src/modelSchema/teamList.ts new file mode 100644 index 000000000..4f3b727b1 --- /dev/null +++ b/frontend/src/modelSchema/teamList.ts @@ -0,0 +1,11 @@ +import type {TypeOf} from 'zod' + +import {IdSchema} from './common/id' + +import {TeamShareBaseSchema} from './teamShareBase' + +export const TeamListSchema = TeamShareBaseSchema.extend({ + listId: IdSchema.default(0), // IList['id'] +}) + +export type TeamList = TypeOf diff --git a/frontend/src/modelSchema/teamMember.ts b/frontend/src/modelSchema/teamMember.ts new file mode 100644 index 000000000..e2b3d05ff --- /dev/null +++ b/frontend/src/modelSchema/teamMember.ts @@ -0,0 +1,13 @@ +import type {TypeOf} from 'zod' +import {boolean} from 'zod' + +import {IdSchema} from './common/id' + +import {UserSchema} from './user' + +export const TeamMemberSchema = UserSchema.extend({ + admin: boolean().default(false), + teamId: IdSchema.default(0), // IList['id'] +}) + +export type TeamMember = TypeOf \ No newline at end of file diff --git a/frontend/src/modelSchema/teamNamespace.ts b/frontend/src/modelSchema/teamNamespace.ts new file mode 100644 index 000000000..6bf477b0c --- /dev/null +++ b/frontend/src/modelSchema/teamNamespace.ts @@ -0,0 +1,11 @@ +import type {TypeOf} from 'zod' + +import {IdSchema} from './common/id' + +import {TeamShareBaseSchema} from './teamShareBase' + +export const TeamNamespaceSchema = TeamShareBaseSchema.extend({ + namespaceId: IdSchema.default(0), // INamespace['id'] +}) + +export type ITeamNamespace = TypeOf diff --git a/frontend/src/modelSchema/teamShareBase.ts b/frontend/src/modelSchema/teamShareBase.ts new file mode 100644 index 000000000..7349582ba --- /dev/null +++ b/frontend/src/modelSchema/teamShareBase.ts @@ -0,0 +1,19 @@ +import type {TypeOf} from 'zod' +import {nativeEnum} from 'zod' + +import {IdSchema} from './common/id' +import {DateSchema} from './common/date' + +import {AbstractSchema} from './abstract' + +import {RIGHTS} from '@/constants/rights' + +export const TeamShareBaseSchema = AbstractSchema.extend({ + teamId: IdSchema.default(0), // ITeam['id'] + right: nativeEnum(RIGHTS).default(RIGHTS.READ), + + created: DateSchema.nullable(), + updated: DateSchema.nullable(), +}) + +export type ITeamShareBase = TypeOf diff --git a/frontend/src/modelSchema/token.ts b/frontend/src/modelSchema/token.ts new file mode 100644 index 000000000..d0ec06ea4 --- /dev/null +++ b/frontend/src/modelSchema/token.ts @@ -0,0 +1,9 @@ +import type {TypeOf} from 'zod' + +import {object, string} from 'zod' + +export const TokenSchema = object({ + token: string(), +}) + +export type IToken = TypeOf \ No newline at end of file diff --git a/frontend/src/modelSchema/totp.ts b/frontend/src/modelSchema/totp.ts new file mode 100644 index 000000000..f406514bd --- /dev/null +++ b/frontend/src/modelSchema/totp.ts @@ -0,0 +1,12 @@ +import type {TypeOf} from 'zod' +import {string, boolean} from 'zod' + +import {AbstractSchema} from './abstract' + +export const TotpSchema = AbstractSchema.extend({ + secret: string().default(''), + enabled: boolean().default(false), + url: string().url().default(''), +}) + +export type Totp = TypeOf diff --git a/frontend/src/modelSchema/user.ts b/frontend/src/modelSchema/user.ts new file mode 100644 index 000000000..5429794cf --- /dev/null +++ b/frontend/src/modelSchema/user.ts @@ -0,0 +1,21 @@ +import type {TypeOf} from 'zod' +import {string} from 'zod' + +import {IdSchema} from './common/id' +import {DateSchema} from './common/date' + +import {AbstractSchema} from './abstract' +import {UserSettingsSchema} from './userSettings' + +export const UserSchema = AbstractSchema.extend({ + id: IdSchema.default(0), + email: string().email().default(''), + username: string().default(''), + name: string().default(''), + settings: UserSettingsSchema.nullable(), + + created: DateSchema.nullable(), + updated: DateSchema.nullable(), +}) + +export type User = TypeOf diff --git a/frontend/src/modelSchema/userList.ts b/frontend/src/modelSchema/userList.ts new file mode 100644 index 000000000..a97bf1423 --- /dev/null +++ b/frontend/src/modelSchema/userList.ts @@ -0,0 +1,11 @@ +import type {TypeOf} from 'zod' + +import {IdSchema} from './common/id' + +import {UserShareBaseSchema} from './userShareBase' + +export const UserListSchema = UserShareBaseSchema.extend({ + listId: IdSchema.default(0), // IList['id'] +}) + +export type IUserList = TypeOf diff --git a/frontend/src/modelSchema/userNamespace.ts b/frontend/src/modelSchema/userNamespace.ts new file mode 100644 index 000000000..b0f4737dc --- /dev/null +++ b/frontend/src/modelSchema/userNamespace.ts @@ -0,0 +1,11 @@ +import type {TypeOf} from 'zod' + +import {IdSchema} from './common/id' + +import {UserShareBaseSchema} from './userShareBase' + +export const UserNamespaceSchema = UserShareBaseSchema.extend({ + namespaceId: IdSchema.default(0), // INamespace['id'] +}) + +export type IUserNamespace = TypeOf diff --git a/frontend/src/modelSchema/userSettings.ts b/frontend/src/modelSchema/userSettings.ts new file mode 100644 index 000000000..898f0535a --- /dev/null +++ b/frontend/src/modelSchema/userSettings.ts @@ -0,0 +1,29 @@ +import type {TypeOf} from 'zod' +import {boolean, string, undefined, nativeEnum} from 'zod' + +import {IdSchema} from './common/id' + +import {AbstractSchema} from './abstract' + +const WEEKDAYS = { + MONDAY: 0, + TUESDAY: 1, + WEDNESDAY: 2, + THURSDAY: 3, + FRIDAY: 4, + SATURDAY: 5, + SUNDAY: 6, +} as const + +export const UserSettingsSchema = AbstractSchema.extend({ + name: string().default(''), + emailRemindersEnabled: boolean().default(true), + discoverableByName: boolean().default(false), + discoverableByEmail: boolean().default(false), + overdueTasksRemindersEnabled: boolean().default(true), + defaultListId: IdSchema.or(undefined()), // iListSchema['id'] // FIXME: shouldn't this be `null`? + weekStart: nativeEnum(WEEKDAYS).default(WEEKDAYS.MONDAY), + timezone: string().default(''), +}) + +export type IUserSettings = TypeOf diff --git a/frontend/src/modelSchema/userShareBase.ts b/frontend/src/modelSchema/userShareBase.ts new file mode 100644 index 000000000..5583026ef --- /dev/null +++ b/frontend/src/modelSchema/userShareBase.ts @@ -0,0 +1,19 @@ +import type {TypeOf} from 'zod' +import {nativeEnum} from 'zod' + +import {IdSchema} from './common/id' +import {DateSchema} from './common/date' + +import {AbstractSchema} from './abstract' + +import {RIGHTS} from '@/constants/rights' + +export const UserShareBaseSchema = AbstractSchema.extend({ + userId: IdSchema, // FIXME: default of model is `''` + right: nativeEnum(RIGHTS).default(RIGHTS.READ), + + created: DateSchema.nullable(), + updated: DateSchema.nullable(), +}) + +export type TeamMember = TypeOf diff --git a/frontend/src/modelTypes/IAvatar.ts b/frontend/src/modelTypes/IAvatar.ts index ab681996a..97bcb8198 100644 --- a/frontend/src/modelTypes/IAvatar.ts +++ b/frontend/src/modelTypes/IAvatar.ts @@ -1,6 +1,13 @@ import type {IAbstract} from './IAbstract' -export type AvatarProvider = 'default' | 'initials' | 'gravatar' | 'marble' | 'upload' +export const AVATAR_PROVIDER = [ + 'default', + 'initials', + 'gravatar', + 'marble', + 'upload', +] as const +export type AvatarProvider = typeof AVATAR_PROVIDER[number] export interface IAvatar extends IAbstract { avatarProvider: AvatarProvider