This commit is contained in:
parent
1a11b43ca8
commit
8a5017b2d6
|
@ -50,6 +50,7 @@
|
||||||
"snake-case": "3.0.4",
|
"snake-case": "3.0.4",
|
||||||
"ufo": "0.8.5",
|
"ufo": "0.8.5",
|
||||||
"v-tooltip": "4.0.0-beta.17",
|
"v-tooltip": "4.0.0-beta.17",
|
||||||
|
"validator": "^13.7.0",
|
||||||
"vue": "3.2.39",
|
"vue": "3.2.39",
|
||||||
"vue-advanced-cropper": "2.8.3",
|
"vue-advanced-cropper": "2.8.3",
|
||||||
"vue-drag-resize": "2.0.3",
|
"vue-drag-resize": "2.0.3",
|
||||||
|
@ -66,6 +67,7 @@
|
||||||
"@cypress/vue": "4.2.0",
|
"@cypress/vue": "4.2.0",
|
||||||
"@faker-js/faker": "7.5.0",
|
"@faker-js/faker": "7.5.0",
|
||||||
"@types/flexsearch": "0.7.3",
|
"@types/flexsearch": "0.7.3",
|
||||||
|
"@types/validator": "^13.7.5",
|
||||||
"@typescript-eslint/eslint-plugin": "5.37.0",
|
"@typescript-eslint/eslint-plugin": "5.37.0",
|
||||||
"@typescript-eslint/parser": "5.37.0",
|
"@typescript-eslint/parser": "5.37.0",
|
||||||
"@vitejs/plugin-legacy": "2.1.0",
|
"@vitejs/plugin-legacy": "2.1.0",
|
||||||
|
@ -95,7 +97,8 @@
|
||||||
"vitest": "0.23.2",
|
"vitest": "0.23.2",
|
||||||
"vue-tsc": "0.40.13",
|
"vue-tsc": "0.40.13",
|
||||||
"wait-on": "6.0.1",
|
"wait-on": "6.0.1",
|
||||||
"workbox-cli": "6.5.4"
|
"workbox-cli": "6.5.4",
|
||||||
|
"zod": "3.18.0"
|
||||||
},
|
},
|
||||||
"postcss": {
|
"postcss": {
|
||||||
"plugins": {
|
"plugins": {
|
||||||
|
|
12
src/modelSchema/LabelTask.ts
Normal file
12
src/modelSchema/LabelTask.ts
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
import type { TypeOf } from 'zod'
|
||||||
|
|
||||||
|
import { AbstractSchema } from './abstract'
|
||||||
|
import { IdSchema } from './common/id'
|
||||||
|
|
||||||
|
export const LabelTaskSchema = AbstractSchema.extend({
|
||||||
|
id: IdSchema.nullable(),
|
||||||
|
taskId: IdSchema.nullable(),
|
||||||
|
labelId: IdSchema.nullable(),
|
||||||
|
})
|
||||||
|
|
||||||
|
export type LabelTask = TypeOf<typeof LabelTaskSchema>
|
9
src/modelSchema/abstract.ts
Normal file
9
src/modelSchema/abstract.ts
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
import { object, nativeEnum, type TypeOf } from 'zod'
|
||||||
|
|
||||||
|
import {RIGHTS} from '@/constants/rights'
|
||||||
|
|
||||||
|
export const AbstractSchema = object({
|
||||||
|
maxRight: nativeEnum(RIGHTS).nullable(),
|
||||||
|
})
|
||||||
|
|
||||||
|
export type IAbstract = TypeOf<typeof AbstractSchema>
|
19
src/modelSchema/attachment.ts
Normal file
19
src/modelSchema/attachment.ts
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
import type { TypeOf } from 'zod'
|
||||||
|
|
||||||
|
import { DateSchema } from './common/date'
|
||||||
|
import { IdSchema } from './common/id'
|
||||||
|
|
||||||
|
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<typeof AttachmentSchema>
|
11
src/modelSchema/avatar.ts
Normal file
11
src/modelSchema/avatar.ts
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
import { z, type TypeOf, 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<typeof AvatarProviderSchema>
|
||||||
|
|
||||||
|
export const AvatarSchema = object({
|
||||||
|
// FIXME: shouldn't the default be 'default'?
|
||||||
|
avatarProvider: string().or(AvatarProviderSchema).default(''),
|
||||||
|
})
|
||||||
|
export type IAvatar = TypeOf<typeof AvatarSchema>
|
18
src/modelSchema/backgroundImage.ts
Normal file
18
src/modelSchema/backgroundImage.ts
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
import { type TypeOf, 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<typeof BackgroundImageSchema>
|
23
src/modelSchema/bucket.ts
Normal file
23
src/modelSchema/bucket.ts
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
import { type TypeOf, number, array, boolean } from 'zod'
|
||||||
|
import { UserSchema } from './user'
|
||||||
|
import { TaskSchema } from './task'
|
||||||
|
import { DateSchema } from './common/date'
|
||||||
|
import { IdSchema } from './common/id'
|
||||||
|
import { AbstractSchema } from './abstract'
|
||||||
|
import { TextFieldSchema } from './common/textField'
|
||||||
|
|
||||||
|
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<typeof BucketSchema>
|
14
src/modelSchema/caldavToken.ts
Normal file
14
src/modelSchema/caldavToken.ts
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
|
||||||
|
import type { TypeOf } from 'zod'
|
||||||
|
|
||||||
|
import { DateSchema } from './common/date'
|
||||||
|
import { IdSchema } from './common/id'
|
||||||
|
|
||||||
|
import { AbstractSchema } from './abstract'
|
||||||
|
|
||||||
|
export const CaldavTokenSchema = AbstractSchema.extend({
|
||||||
|
id: IdSchema,
|
||||||
|
created: DateSchema,
|
||||||
|
})
|
||||||
|
|
||||||
|
export type CaldavToken = TypeOf<typeof CaldavTokenSchema>
|
21
src/modelSchema/common/RelationKind.ts
Normal file
21
src/modelSchema/common/RelationKind.ts
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
import { nativeEnum, type TypeOf } 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<typeof RelationKindSchema>
|
||||||
|
|
11
src/modelSchema/common/date.ts
Normal file
11
src/modelSchema/common/date.ts
Normal file
|
@ -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())
|
79
src/modelSchema/common/filter.ts
Normal file
79
src/modelSchema/common/filter.ts
Normal file
|
@ -0,0 +1,79 @@
|
||||||
|
import { z, nativeEnum, type TypeOf, 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<typeof FilterSchema>
|
11
src/modelSchema/common/hexColor.ts
Normal file
11
src/modelSchema/common/hexColor.ts
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
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))
|
6
src/modelSchema/common/id.ts
Normal file
6
src/modelSchema/common/id.ts
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
import { number, preprocess } from 'zod'
|
||||||
|
|
||||||
|
export const IdSchema = preprocess(
|
||||||
|
(value: unknown) => Number(value),
|
||||||
|
number().positive().int(),
|
||||||
|
)
|
|
@ -1,5 +1,19 @@
|
||||||
import { REPEAT_TYPES, type IRepeatAfter } from '@/types/IRepeatAfter'
|
import { nativeEnum, number, object, preprocess, type TypeOf } from 'zod'
|
||||||
import { nativeEnum, number, object, preprocess } from 'zod'
|
|
||||||
|
export enum REPEAT_TYPES {
|
||||||
|
HOURS = 'hours',
|
||||||
|
DAYS = 'days',
|
||||||
|
WEEKS = 'weeks',
|
||||||
|
MONTHS = 'months',
|
||||||
|
YEARS = 'years',
|
||||||
|
}
|
||||||
|
|
||||||
|
export type IRepeatType = typeof REPEAT_TYPES[keyof typeof REPEAT_TYPES]
|
||||||
|
|
||||||
|
export interface IRepeatAfter {
|
||||||
|
type: IRepeatType,
|
||||||
|
amount: number,
|
||||||
|
}
|
||||||
|
|
||||||
export const RepeatsSchema = preprocess(
|
export const RepeatsSchema = preprocess(
|
||||||
(repeats: unknown) => {
|
(repeats: unknown) => {
|
||||||
|
@ -12,7 +26,7 @@ export const RepeatsSchema = preprocess(
|
||||||
const repeatAfterHours = (repeats / 60) / 60
|
const repeatAfterHours = (repeats / 60) / 60
|
||||||
|
|
||||||
const repeatAfter : IRepeatAfter = {
|
const repeatAfter : IRepeatAfter = {
|
||||||
type: 'hours',
|
type: REPEAT_TYPES.HOURS,
|
||||||
amount: repeatAfterHours,
|
amount: repeatAfterHours,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -20,16 +34,16 @@ export const RepeatsSchema = preprocess(
|
||||||
if (repeatAfterHours % 24 === 0) {
|
if (repeatAfterHours % 24 === 0) {
|
||||||
const repeatAfterDays = repeatAfterHours / 24
|
const repeatAfterDays = repeatAfterHours / 24
|
||||||
if (repeatAfterDays % 7 === 0) {
|
if (repeatAfterDays % 7 === 0) {
|
||||||
repeatAfter.type = 'weeks'
|
repeatAfter.type = REPEAT_TYPES.WEEKS
|
||||||
repeatAfter.amount = repeatAfterDays / 7
|
repeatAfter.amount = repeatAfterDays / 7
|
||||||
} else if (repeatAfterDays % 30 === 0) {
|
} else if (repeatAfterDays % 30 === 0) {
|
||||||
repeatAfter.type = 'months'
|
repeatAfter.type = REPEAT_TYPES.MONTHS
|
||||||
repeatAfter.amount = repeatAfterDays / 30
|
repeatAfter.amount = repeatAfterDays / 30
|
||||||
} else if (repeatAfterDays % 365 === 0) {
|
} else if (repeatAfterDays % 365 === 0) {
|
||||||
repeatAfter.type = 'years'
|
repeatAfter.type = REPEAT_TYPES.YEARS
|
||||||
repeatAfter.amount = repeatAfterDays / 365
|
repeatAfter.amount = repeatAfterDays / 365
|
||||||
} else {
|
} else {
|
||||||
repeatAfter.type = 'days'
|
repeatAfter.type = REPEAT_TYPES.DAYS
|
||||||
repeatAfter.amount = repeatAfterDays
|
repeatAfter.amount = repeatAfterDays
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -41,3 +55,5 @@ export const RepeatsSchema = preprocess(
|
||||||
amount: number().int(),
|
amount: number().int(),
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
export type RepeatAfter = TypeOf<typeof RepeatsSchema>
|
3
src/modelSchema/common/textField.ts
Normal file
3
src/modelSchema/common/textField.ts
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
import {string} from 'zod'
|
||||||
|
|
||||||
|
export const TextFieldSchema = string().transform((value) => value.trim()).default('')
|
9
src/modelSchema/emailUpdate.ts
Normal file
9
src/modelSchema/emailUpdate.ts
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
import { type TypeOf, string } from 'zod'
|
||||||
|
import { AbstractSchema } from './abstract'
|
||||||
|
|
||||||
|
export const EmailUpdateSchema = AbstractSchema.extend({
|
||||||
|
newEmail: string().email().default(''),
|
||||||
|
password: string().default(''),
|
||||||
|
})
|
||||||
|
|
||||||
|
export type EmailUpdate = TypeOf<typeof EmailUpdateSchema>
|
13
src/modelSchema/file.ts
Normal file
13
src/modelSchema/file.ts
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
import { type TypeOf, object, number, string } from 'zod'
|
||||||
|
import { DateSchema } from './common/date'
|
||||||
|
import { IdSchema } from './common/id'
|
||||||
|
|
||||||
|
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<typeof FileSchema>
|
32
src/modelSchema/label.ts
Normal file
32
src/modelSchema/label.ts
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
import { type TypeOf, string } from 'zod'
|
||||||
|
|
||||||
|
import { UserSchema } from './user'
|
||||||
|
|
||||||
|
import { DateSchema } from './common/date'
|
||||||
|
import { IdSchema } from './common/id'
|
||||||
|
import { HexColorSchema } from './common/hexColor'
|
||||||
|
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<typeof LabelSchema>
|
22
src/modelSchema/linkShare.ts
Normal file
22
src/modelSchema/linkShare.ts
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
import { type TypeOf, number, string, nativeEnum } from 'zod'
|
||||||
|
import { UserSchema } from './user'
|
||||||
|
import { IdSchema } from './common/id'
|
||||||
|
import { RIGHTS } from '@/constants/rights'
|
||||||
|
import { DateSchema } from './common/date'
|
||||||
|
import { AbstractSchema } from './abstract'
|
||||||
|
|
||||||
|
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<typeof LinkShareSchema>
|
31
src/modelSchema/list.ts
Normal file
31
src/modelSchema/list.ts
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
import { type TypeOf, boolean, number, string, array, any } from 'zod'
|
||||||
|
import { AbstractSchema } from './abstract'
|
||||||
|
|
||||||
|
import { DateSchema } from './common/date'
|
||||||
|
import { IdSchema } from './common/id'
|
||||||
|
|
||||||
|
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<typeof ListSchema>
|
13
src/modelSchema/listDuplication.ts
Normal file
13
src/modelSchema/listDuplication.ts
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
import type { TypeOf } from 'zod'
|
||||||
|
|
||||||
|
import { AbstractSchema } from './abstract'
|
||||||
|
import { IdSchema } from './common/id'
|
||||||
|
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<typeof ListDuplicationSchema>
|
24
src/modelSchema/namespace.ts
Normal file
24
src/modelSchema/namespace.ts
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
import { type TypeOf, boolean, string, array } from 'zod'
|
||||||
|
|
||||||
|
import { AbstractSchema } from './abstract'
|
||||||
|
import { ListSchema } from './list'
|
||||||
|
import { UserSchema } from './user'
|
||||||
|
import { SubscriptionSchema } from './subscription'
|
||||||
|
import { IdSchema } from './common/id'
|
||||||
|
import { HexColorSchema } from './common/hexColor'
|
||||||
|
|
||||||
|
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<typeof NamespaceSchema>
|
51
src/modelSchema/notification.ts
Normal file
51
src/modelSchema/notification.ts
Normal file
|
@ -0,0 +1,51 @@
|
||||||
|
import { type TypeOf, union, boolean, object, string } from 'zod'
|
||||||
|
import { AbstractSchema } from './abstract'
|
||||||
|
import { DateSchema } from './common/date'
|
||||||
|
import { IdSchema } from './common/id'
|
||||||
|
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<typeof NotificationSchema>
|
12
src/modelSchema/passwordReset.ts
Normal file
12
src/modelSchema/passwordReset.ts
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
import { type TypeOf, 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<typeof PasswordResetSchema>
|
13
src/modelSchema/passwordUpdate.ts
Normal file
13
src/modelSchema/passwordUpdate.ts
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
import { type TypeOf, 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<typeof PasswordUpdateSchema>
|
20
src/modelSchema/savedFilter.ts
Normal file
20
src/modelSchema/savedFilter.ts
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
import { type TypeOf, string } from 'zod'
|
||||||
|
import { AbstractSchema } from './abstract'
|
||||||
|
import { DateSchema } from './common/date'
|
||||||
|
import { FilterSchema } from './common/filter'
|
||||||
|
import { IdSchema } from './common/id'
|
||||||
|
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<typeof SavedFilterSchema>
|
18
src/modelSchema/subscription.ts
Normal file
18
src/modelSchema/subscription.ts
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
import { type TypeOf, 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<typeof SubscriptionSchema>
|
79
src/modelSchema/task.ts
Normal file
79
src/modelSchema/task.ts
Normal file
|
@ -0,0 +1,79 @@
|
||||||
|
import { type ZodType, type TypeOf,nativeEnum, boolean, number, string, array, record, unknown, lazy } from 'zod'
|
||||||
|
import { AttachmentSchema } from './attachment'
|
||||||
|
import { LabelSchema } from './label'
|
||||||
|
import { UserSchema } from './user'
|
||||||
|
import { SubscriptionSchema } from './subscription'
|
||||||
|
|
||||||
|
import { PRIORITIES } from '@/constants/priorities'
|
||||||
|
|
||||||
|
import { RepeatsSchema } from './common/repeats'
|
||||||
|
import { DateSchema } from './common/date'
|
||||||
|
import { IdSchema } from './common/id'
|
||||||
|
import { HexColorSchema } from './common/hexColor'
|
||||||
|
import { AbstractSchema } from './abstract'
|
||||||
|
import { TextFieldSchema } from './common/textField'
|
||||||
|
import { TASK_REPEAT_MODES } from '@/types/IRepeatMode'
|
||||||
|
import { RelationKindSchema } from './common/RelationKind'
|
||||||
|
|
||||||
|
const LabelsSchema = array(LabelSchema)
|
||||||
|
.transform((labels) => labels.sort((f, s) => f.title > s.title ? 1 : -1)) // FIXME: use
|
||||||
|
.default([])
|
||||||
|
|
||||||
|
export type ILabels = TypeOf<typeof LabelsSchema>
|
||||||
|
|
||||||
|
const RelatedTasksSchema = record(RelationKindSchema, record(string(), unknown()))
|
||||||
|
export type IRelatedTasksSchema = TypeOf<typeof RelatedTasksSchema>
|
||||||
|
|
||||||
|
const RelatedTasksLazySchema : ZodType<Task['relatedTasks']> = lazy(() =>
|
||||||
|
record(RelationKindSchema, TaskSchema),
|
||||||
|
)
|
||||||
|
export type IRelatedTasksLazySchema = TypeOf<typeof RelatedTasksLazySchema>
|
||||||
|
|
||||||
|
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<typeof TaskSchema>
|
14
src/modelSchema/taskAssignee.ts
Normal file
14
src/modelSchema/taskAssignee.ts
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
import type { TypeOf } from 'zod'
|
||||||
|
|
||||||
|
import { DateSchema } from './common/date'
|
||||||
|
import { IdSchema } from './common/id'
|
||||||
|
|
||||||
|
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<typeof TaskAssigneeSchema>
|
18
src/modelSchema/taskComment.ts
Normal file
18
src/modelSchema/taskComment.ts
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
import { type TypeOf, string } from 'zod'
|
||||||
|
import { AbstractSchema } from './abstract'
|
||||||
|
import { DateSchema } from './common/date'
|
||||||
|
import { IdSchema } from './common/id'
|
||||||
|
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<typeof TaskCommentSchema>
|
21
src/modelSchema/taskRelation.ts
Normal file
21
src/modelSchema/taskRelation.ts
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
import { RELATION_KIND } from '@/types/IRelationKind'
|
||||||
|
import { type TypeOf, nativeEnum } from 'zod'
|
||||||
|
import { AbstractSchema } from './abstract'
|
||||||
|
import { DateSchema } from './common/date'
|
||||||
|
import { IdSchema } from './common/id'
|
||||||
|
import { UserSchema } from './user'
|
||||||
|
|
||||||
|
|
||||||
|
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<typeof TaskRelationSchema>
|
24
src/modelSchema/team.ts
Normal file
24
src/modelSchema/team.ts
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
import { type TypeOf, array, nativeEnum, string } from 'zod'
|
||||||
|
|
||||||
|
import { RIGHTS } from '@/constants/rights'
|
||||||
|
|
||||||
|
import { DateSchema } from './common/date'
|
||||||
|
import { IdSchema } from './common/id'
|
||||||
|
|
||||||
|
import { AbstractSchema } from './abstract'
|
||||||
|
import { UserSchema } from './user'
|
||||||
|
import { TeamMemberSchema } from './teamMember'
|
||||||
|
|
||||||
|
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<typeof TeamSchema>
|
11
src/modelSchema/teamList.ts
Normal file
11
src/modelSchema/teamList.ts
Normal file
|
@ -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<typeof TeamListSchema>
|
12
src/modelSchema/teamMember.ts
Normal file
12
src/modelSchema/teamMember.ts
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
import { type TypeOf, 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<typeof TeamMemberSchema>
|
11
src/modelSchema/teamNamespace.ts
Normal file
11
src/modelSchema/teamNamespace.ts
Normal file
|
@ -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<typeof TeamNamespaceSchema>
|
18
src/modelSchema/teamShareBase.ts
Normal file
18
src/modelSchema/teamShareBase.ts
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
import { type TypeOf, nativeEnum } from 'zod'
|
||||||
|
|
||||||
|
import { AbstractSchema } from './abstract'
|
||||||
|
|
||||||
|
import { DateSchema } from './common/date'
|
||||||
|
import { IdSchema } from './common/id'
|
||||||
|
|
||||||
|
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<typeof TeamShareBaseSchema>
|
7
src/modelSchema/token.ts
Normal file
7
src/modelSchema/token.ts
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
import { object, string, type TypeOf } from 'zod'
|
||||||
|
|
||||||
|
export const TokenSchema = object({
|
||||||
|
token: string(),
|
||||||
|
})
|
||||||
|
|
||||||
|
export type IToken = TypeOf<typeof TokenSchema>
|
11
src/modelSchema/totp.ts
Normal file
11
src/modelSchema/totp.ts
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
import { type TypeOf, 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<typeof TotpSchema>
|
20
src/modelSchema/user.ts
Normal file
20
src/modelSchema/user.ts
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
import { type TypeOf, 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<typeof UserSchema>
|
11
src/modelSchema/userList.ts
Normal file
11
src/modelSchema/userList.ts
Normal file
|
@ -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<typeof UserListSchema>
|
11
src/modelSchema/userNamespace.ts
Normal file
11
src/modelSchema/userNamespace.ts
Normal file
|
@ -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<typeof UserNamespaceSchema>
|
28
src/modelSchema/userSettings.ts
Normal file
28
src/modelSchema/userSettings.ts
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
import { type TypeOf, 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<typeof UserSettingsSchema>
|
17
src/modelSchema/userShareBase.ts
Normal file
17
src/modelSchema/userShareBase.ts
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
import { type TypeOf, nativeEnum } from 'zod'
|
||||||
|
|
||||||
|
import { DateSchema } from './common/date'
|
||||||
|
import { IdSchema } from './common/id'
|
||||||
|
|
||||||
|
import { RIGHTS } from '@/constants/rights'
|
||||||
|
import { AbstractSchema } from './abstract'
|
||||||
|
|
||||||
|
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<typeof UserShareBaseSchema>
|
|
@ -1,6 +1,7 @@
|
||||||
import type {IAbstract} from './IAbstract'
|
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 {
|
export interface IAvatar extends IAbstract {
|
||||||
avatarProvider: AvatarProvider
|
avatarProvider: AvatarProvider
|
||||||
|
|
15
yarn.lock
15
yarn.lock
|
@ -2391,6 +2391,11 @@
|
||||||
resolved "https://registry.yarnpkg.com/@types/trusted-types/-/trusted-types-2.0.2.tgz#fc25ad9943bcac11cceb8168db4f275e0e72e756"
|
resolved "https://registry.yarnpkg.com/@types/trusted-types/-/trusted-types-2.0.2.tgz#fc25ad9943bcac11cceb8168db4f275e0e72e756"
|
||||||
integrity sha512-F5DIZ36YVLE+PN+Zwws4kJogq47hNgX3Nx6WyDJ3kcplxyke3XIzB8uK5n/Lpm1HBsbGzd6nmGehL8cPekP+Tg==
|
integrity sha512-F5DIZ36YVLE+PN+Zwws4kJogq47hNgX3Nx6WyDJ3kcplxyke3XIzB8uK5n/Lpm1HBsbGzd6nmGehL8cPekP+Tg==
|
||||||
|
|
||||||
|
"@types/validator@^13.7.5":
|
||||||
|
version "13.7.5"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/validator/-/validator-13.7.5.tgz#96ad8c50d441c8c86a639efccc4d8d9d5d9cd5ea"
|
||||||
|
integrity sha512-9rQHeAqz6Jw3gDhttkmWetoriW5FPbxylv/6h6mXtaj2NKRcOvOmvfcswVdLVpbuy10NrO486K3lCoLgoIhiIA==
|
||||||
|
|
||||||
"@types/web-bluetooth@^0.0.15":
|
"@types/web-bluetooth@^0.0.15":
|
||||||
version "0.0.15"
|
version "0.0.15"
|
||||||
resolved "https://registry.yarnpkg.com/@types/web-bluetooth/-/web-bluetooth-0.0.15.tgz#d60330046a6ed8a13b4a53df3813c44942ebdf72"
|
resolved "https://registry.yarnpkg.com/@types/web-bluetooth/-/web-bluetooth-0.0.15.tgz#d60330046a6ed8a13b4a53df3813c44942ebdf72"
|
||||||
|
@ -12499,6 +12504,11 @@ validate-npm-package-name@^4.0.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
builtins "^5.0.0"
|
builtins "^5.0.0"
|
||||||
|
|
||||||
|
validator@^13.7.0:
|
||||||
|
version "13.7.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/validator/-/validator-13.7.0.tgz#4f9658ba13ba8f3d82ee881d3516489ea85c0857"
|
||||||
|
integrity sha512-nYXQLCBkpJ8X6ltALua9dRrZDHVYxjJ1wgskNt1lH9fzGjs3tgojGSCBjmEPwkWS1y29+DrizMTW19Pr9uB2nw==
|
||||||
|
|
||||||
vary@~1.1.2:
|
vary@~1.1.2:
|
||||||
version "1.1.2"
|
version "1.1.2"
|
||||||
resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc"
|
resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc"
|
||||||
|
@ -13304,3 +13314,8 @@ zip-stream@^4.1.0:
|
||||||
archiver-utils "^2.1.0"
|
archiver-utils "^2.1.0"
|
||||||
compress-commons "^4.1.0"
|
compress-commons "^4.1.0"
|
||||||
readable-stream "^3.6.0"
|
readable-stream "^3.6.0"
|
||||||
|
|
||||||
|
zod@3.18.0:
|
||||||
|
version "3.18.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/zod/-/zod-3.18.0.tgz#2eed58b3cafb8d9a67aa2fee69279702f584f3bc"
|
||||||
|
integrity sha512-gwTm8RfUCe8l9rDwN5r2A17DkAa8Ez4Yl4yXqc5VqeGaXaJahzYYXbTwvhroZi0SNBqTwh/bKm2N0mpCzuw4bA==
|
||||||
|
|
Reference in New Issue
Block a user