feat: improve types
This commit is contained in:
parent
42e72d14a4
commit
c9e85cb52b
14 changed files with 52 additions and 45 deletions
|
@ -200,7 +200,7 @@ function handleEnter(e: KeyboardEvent) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function focusTaskInput() {
|
function focusTaskInput() {
|
||||||
newTaskInput.value.focus()
|
newTaskInput.value?.focus()
|
||||||
}
|
}
|
||||||
|
|
||||||
defineExpose({
|
defineExpose({
|
||||||
|
|
|
@ -117,7 +117,7 @@ async function removeLabel(label: LabelModel) {
|
||||||
|
|
||||||
for (const l in labels.value) {
|
for (const l in labels.value) {
|
||||||
if (labels.value[l].id === label.id) {
|
if (labels.value[l].id === label.id) {
|
||||||
labels.value.splice(l, 1)
|
labels.value.splice(l, 1) // FIXME: l should be index
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
emit('update:modelValue', labels.value)
|
emit('update:modelValue', labels.value)
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<template>
|
<template>
|
||||||
<multiselect
|
<Multiselect
|
||||||
class="control is-expanded"
|
class="control is-expanded"
|
||||||
:placeholder="$t('list.search')"
|
:placeholder="$t('list.search')"
|
||||||
@search="findLists"
|
@search="findLists"
|
||||||
|
@ -13,7 +13,7 @@
|
||||||
<span class="list-namespace-title search-result">{{ namespace(props.option.namespaceId) }} ></span>
|
<span class="list-namespace-title search-result">{{ namespace(props.option.namespaceId) }} ></span>
|
||||||
{{ props.option.title }}
|
{{ props.option.title }}
|
||||||
</template>
|
</template>
|
||||||
</multiselect>
|
</Multiselect>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
|
|
|
@ -45,8 +45,8 @@ const props = defineProps({
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
const isDate = (e: any) => e instanceof Date
|
const isDate = (e: unknown) => e instanceof Date
|
||||||
const isString = (e: any) => typeof e === 'string'
|
const isString = (e: unknown) => typeof e === 'string'
|
||||||
|
|
||||||
for (const e of prop) {
|
for (const e of prop) {
|
||||||
if (!isDate(e) && !isString(e)) {
|
if (!isDate(e) && !isString(e)) {
|
||||||
|
|
|
@ -29,6 +29,6 @@ export async function uploadFiles(attachmentService: AttachmentService, taskId:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function generateAttachmentUrl(taskId: number, attachmentId: number) : any {
|
export function generateAttachmentUrl(taskId: number, attachmentId: number) {
|
||||||
return `${window.API_URL}/tasks/${taskId}/attachments/${attachmentId}`
|
return `${window.API_URL}/tasks/${taskId}/attachments/${attachmentId}`
|
||||||
}
|
}
|
|
@ -11,11 +11,11 @@ export function includesById(array: [], id: string | number) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// https://github.com/you-dont-need/You-Dont-Need-Lodash-Underscore#_isnil
|
// https://github.com/you-dont-need/You-Dont-Need-Lodash-Underscore#_isnil
|
||||||
export function isNil(value: any) {
|
export function isNil(value: unknown) {
|
||||||
return value == null
|
return value == null
|
||||||
}
|
}
|
||||||
|
|
||||||
export function omitBy(obj: {}, check: (value: any) => boolean): {} {
|
export function omitBy(obj: {}, check: (value: unknown) => boolean) {
|
||||||
if (isNil(obj)) {
|
if (isNil(obj)) {
|
||||||
return {}
|
return {}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,3 +6,5 @@ export const PRIORITIES = {
|
||||||
'URGENT': 4,
|
'URGENT': 4,
|
||||||
'DO_NOW': 5,
|
'DO_NOW': 5,
|
||||||
} as const
|
} as const
|
||||||
|
|
||||||
|
export type Priority = typeof PRIORITIES[keyof typeof PRIORITIES]
|
|
@ -6,6 +6,7 @@ import AttachmentModel from './attachment'
|
||||||
import SubscriptionModel from '@/models/subscription'
|
import SubscriptionModel from '@/models/subscription'
|
||||||
import {parseDateOrNull} from '@/helpers/parseDateOrNull'
|
import {parseDateOrNull} from '@/helpers/parseDateOrNull'
|
||||||
import type ListModel from './list'
|
import type ListModel from './list'
|
||||||
|
import type { Priority } from './constants/priorities'
|
||||||
|
|
||||||
const SUPPORTS_TRIGGERED_NOTIFICATION = 'Notification' in window && 'showTrigger' in Notification.prototype
|
const SUPPORTS_TRIGGERED_NOTIFICATION = 'Notification' in window && 'showTrigger' in Notification.prototype
|
||||||
export const TASK_DEFAULT_COLOR = '#1973ff'
|
export const TASK_DEFAULT_COLOR = '#1973ff'
|
||||||
|
@ -29,7 +30,7 @@ export default class TaskModel extends AbstractModel {
|
||||||
description: string
|
description: string
|
||||||
done: boolean
|
done: boolean
|
||||||
doneAt: Date | null
|
doneAt: Date | null
|
||||||
priority: 0
|
priority: Priority
|
||||||
labels: LabelModel[]
|
labels: LabelModel[]
|
||||||
assignees: UserModel[]
|
assignees: UserModel[]
|
||||||
|
|
||||||
|
|
|
@ -2,18 +2,20 @@ import AbstractModel from './abstractModel'
|
||||||
import UserModel from './user'
|
import UserModel from './user'
|
||||||
import type TaskModel from './task'
|
import type TaskModel from './task'
|
||||||
|
|
||||||
export const RELATION_KINDS = [
|
export const RELATION_KIND = {
|
||||||
'subtask',
|
'SUBTASK': 'subtask',
|
||||||
'parenttask',
|
'PARENTTASK': 'parenttask',
|
||||||
'related',
|
'RELATED': 'related',
|
||||||
'duplicates',
|
'DUPLICATES': 'duplicates',
|
||||||
'blocking',
|
'BLOCKING': 'blocking',
|
||||||
'blocked',
|
'BLOCKED': 'blocked',
|
||||||
'precedes',
|
'PROCEDES': 'precedes',
|
||||||
'follows',
|
'FOLLOWS': 'follows',
|
||||||
'copiedfrom',
|
'COPIEDFROM': 'copiedfrom',
|
||||||
'copiedto',
|
'COPIEDTO': 'copiedto',
|
||||||
] as const
|
} as const
|
||||||
|
|
||||||
|
export const RELATION_KINDS = [...Object.values(RELATION_KIND)] as const
|
||||||
|
|
||||||
export type RelationKind = typeof RELATION_KINDS[number]
|
export type RelationKind = typeof RELATION_KINDS[number]
|
||||||
|
|
||||||
|
|
|
@ -47,7 +47,7 @@ interface repeatParsedResult {
|
||||||
repeats: Repeats | null,
|
repeats: Repeats | null,
|
||||||
}
|
}
|
||||||
|
|
||||||
interface ParsedTaskText {
|
export interface ParsedTaskText {
|
||||||
text: string,
|
text: string,
|
||||||
date: Date | null,
|
date: Date | null,
|
||||||
labels: string[],
|
labels: string[],
|
||||||
|
|
|
@ -3,6 +3,8 @@ import type {Method} from 'axios'
|
||||||
|
|
||||||
import {objectToSnakeCase} from '@/helpers/case'
|
import {objectToSnakeCase} from '@/helpers/case'
|
||||||
import AbstractModel from '@/models/abstractModel'
|
import AbstractModel from '@/models/abstractModel'
|
||||||
|
import type { Right } from '@/models/constants/rights'
|
||||||
|
import type FileModel from '@/models/file'
|
||||||
|
|
||||||
interface Paths {
|
interface Paths {
|
||||||
create : string
|
create : string
|
||||||
|
@ -20,7 +22,7 @@ function convertObject(o: Record<string, unknown>) {
|
||||||
return o
|
return o
|
||||||
}
|
}
|
||||||
|
|
||||||
function prepareParams(params: Record<string, unknown | any[]>) {
|
function prepareParams(params: Record<string, unknown | unknown[]>) {
|
||||||
if (typeof params !== 'object') {
|
if (typeof params !== 'object') {
|
||||||
return params
|
return params
|
||||||
}
|
}
|
||||||
|
@ -37,7 +39,7 @@ function prepareParams(params: Record<string, unknown | any[]>) {
|
||||||
return objectToSnakeCase(params)
|
return objectToSnakeCase(params)
|
||||||
}
|
}
|
||||||
|
|
||||||
export default class AbstractService<Model extends AbstractModel> {
|
export default class AbstractService<Model extends AbstractModel = AbstractModel> {
|
||||||
|
|
||||||
/////////////////////////////
|
/////////////////////////////
|
||||||
// Initial variable definitions
|
// Initial variable definitions
|
||||||
|
@ -124,8 +126,8 @@ export default class AbstractService<Model extends AbstractModel> {
|
||||||
/**
|
/**
|
||||||
* Returns an object with all route parameters and their values.
|
* Returns an object with all route parameters and their values.
|
||||||
*/
|
*/
|
||||||
getRouteReplacements(route : string, parameters = {}) {
|
getRouteReplacements(route : string, parameters : Record<string, unknown> = {}) {
|
||||||
const replace$$1: {} = {}
|
const replace$$1: Record<string, unknown> = {}
|
||||||
let pattern = this.getRouteParameterPattern()
|
let pattern = this.getRouteParameterPattern()
|
||||||
pattern = new RegExp(pattern instanceof RegExp ? pattern.source : pattern, 'g')
|
pattern = new RegExp(pattern instanceof RegExp ? pattern.source : pattern, 'g')
|
||||||
|
|
||||||
|
@ -161,7 +163,7 @@ export default class AbstractService<Model extends AbstractModel> {
|
||||||
* But because the timeout is created using setTimeout, it will still trigger even if the request is
|
* But because the timeout is created using setTimeout, it will still trigger even if the request is
|
||||||
* already finished, so we return a method to call in that case.
|
* already finished, so we return a method to call in that case.
|
||||||
*/
|
*/
|
||||||
setLoading(): Function {
|
setLoading() {
|
||||||
const timeout = setTimeout(() => {
|
const timeout = setTimeout(() => {
|
||||||
this.loading = true
|
this.loading = true
|
||||||
}, 100)
|
}, 100)
|
||||||
|
@ -267,7 +269,7 @@ export default class AbstractService<Model extends AbstractModel> {
|
||||||
* This is a more abstract implementation which only does a get request.
|
* This is a more abstract implementation which only does a get request.
|
||||||
* Services which need more flexibility can use this.
|
* Services which need more flexibility can use this.
|
||||||
*/
|
*/
|
||||||
async getM(url : string, model = new AbstractModel({}), params = {}) {
|
async getM(url : string, model = new AbstractModel({}) as Model, params: Record<string, unknown> = {}) {
|
||||||
const cancel = this.setLoading()
|
const cancel = this.setLoading()
|
||||||
|
|
||||||
model = this.beforeGet(model)
|
model = this.beforeGet(model)
|
||||||
|
@ -276,7 +278,7 @@ export default class AbstractService<Model extends AbstractModel> {
|
||||||
try {
|
try {
|
||||||
const response = await this.http.get(finalUrl, {params: prepareParams(params)})
|
const response = await this.http.get(finalUrl, {params: prepareParams(params)})
|
||||||
const result = this.modelGetFactory(response.data)
|
const result = this.modelGetFactory(response.data)
|
||||||
result.maxRight = Number(response.headers['x-max-right'])
|
result.maxRight = Number(response.headers['x-max-right']) as Right
|
||||||
return result
|
return result
|
||||||
} finally {
|
} finally {
|
||||||
cancel()
|
cancel()
|
||||||
|
@ -300,7 +302,7 @@ export default class AbstractService<Model extends AbstractModel> {
|
||||||
* @param params Optional query parameters
|
* @param params Optional query parameters
|
||||||
* @param page The page to get
|
* @param page The page to get
|
||||||
*/
|
*/
|
||||||
async getAll(model : Model = new AbstractModel({}), params = {}, page = 1) {
|
async getAll(model : Model = new AbstractModel({}) as Model, params = {}, page = 1) {
|
||||||
if (this.paths.getAll === '') {
|
if (this.paths.getAll === '') {
|
||||||
throw new Error('This model is not able to get data.')
|
throw new Error('This model is not able to get data.')
|
||||||
}
|
}
|
||||||
|
@ -406,10 +408,10 @@ export default class AbstractService<Model extends AbstractModel> {
|
||||||
/**
|
/**
|
||||||
* Uploads a file to a url.
|
* Uploads a file to a url.
|
||||||
* @param url
|
* @param url
|
||||||
* @param file
|
* @param file {FileModel}
|
||||||
* @param fieldName The name of the field the file is uploaded to.
|
* @param fieldName The name of the field the file is uploaded to.
|
||||||
*/
|
*/
|
||||||
uploadFile(url : string, file, fieldName : string) {
|
uploadFile(url : string, file: FileModel, fieldName : string) {
|
||||||
return this.uploadBlob(url, new Blob([file]), fieldName, file.name)
|
return this.uploadBlob(url, new Blob([file]), fieldName, file.name)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -425,7 +427,7 @@ export default class AbstractService<Model extends AbstractModel> {
|
||||||
/**
|
/**
|
||||||
* Uploads a form data object.
|
* Uploads a form data object.
|
||||||
*/
|
*/
|
||||||
async uploadFormData(url : string, formData: Record<string, unknown>) {
|
async uploadFormData(url : string, formData: FormData) {
|
||||||
const cancel = this.setLoading()
|
const cancel = this.setLoading()
|
||||||
try {
|
try {
|
||||||
const response = await this.http.put(
|
const response = await this.http.put(
|
||||||
|
|
|
@ -2,8 +2,9 @@ import AbstractService from './abstractService'
|
||||||
import AttachmentModel from '../models/attachment'
|
import AttachmentModel from '../models/attachment'
|
||||||
import {formatISO} from 'date-fns'
|
import {formatISO} from 'date-fns'
|
||||||
import {downloadBlob} from '@/helpers/downloadBlob'
|
import {downloadBlob} from '@/helpers/downloadBlob'
|
||||||
|
import type FileModel from '@/models/file'
|
||||||
|
|
||||||
export default class AttachmentService extends AbstractService {
|
export default class AttachmentService extends AbstractService<AttachmentModel> {
|
||||||
constructor() {
|
constructor() {
|
||||||
super({
|
super({
|
||||||
create: '/tasks/{taskId}/attachments',
|
create: '/tasks/{taskId}/attachments',
|
||||||
|
@ -12,7 +13,7 @@ export default class AttachmentService extends AbstractService {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
processModel(model) {
|
processModel(model: AttachmentModel) {
|
||||||
model.created = formatISO(new Date(model.created))
|
model.created = formatISO(new Date(model.created))
|
||||||
return model
|
return model
|
||||||
}
|
}
|
||||||
|
@ -33,26 +34,25 @@ export default class AttachmentService extends AbstractService {
|
||||||
return data
|
return data
|
||||||
}
|
}
|
||||||
|
|
||||||
getBlobUrl(model) {
|
getBlobUrl(model: AttachmentModel) {
|
||||||
return AbstractService.prototype.getBlobUrl.call(this, '/tasks/' + model.taskId + '/attachments/' + model.id)
|
return AbstractService.prototype.getBlobUrl.call(this, '/tasks/' + model.taskId + '/attachments/' + model.id)
|
||||||
}
|
}
|
||||||
|
|
||||||
async download(model) {
|
async download(model: AttachmentModel) {
|
||||||
const url = await this.getBlobUrl(model)
|
const url = await this.getBlobUrl(model)
|
||||||
return downloadBlob(url, model.file.name)
|
return downloadBlob(url, model.file.name)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Uploads a file to the server
|
* Uploads a file to the server
|
||||||
* @param model
|
|
||||||
* @param files
|
* @param files
|
||||||
* @returns {Promise<any|never>}
|
* @returns {Promise<any|never>}
|
||||||
*/
|
*/
|
||||||
create(model, files) {
|
create(model: AttachmentModel, files: FileModel[]) {
|
||||||
const data = new FormData()
|
const data = new FormData()
|
||||||
for (let i = 0; i < files.length; i++) {
|
for (let i = 0; i < files.length; i++) {
|
||||||
// TODO: Validation of file size
|
// TODO: Validation of file size
|
||||||
data.append('files', new Blob([files[i]]), files[i].name)
|
data.append('files', new Blob([JSON.stringify(files[i], null, 2)]), files[i].name)
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.uploadFormData(
|
return this.uploadFormData(
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import AbstractService from './abstractService'
|
import AbstractService from './abstractService'
|
||||||
import ListModel from '../models/list'
|
import ListModel from '../models/list'
|
||||||
|
import type FileModel from '@/models/file'
|
||||||
|
|
||||||
export default class BackgroundUploadService extends AbstractService {
|
export default class BackgroundUploadService extends AbstractService {
|
||||||
constructor() {
|
constructor() {
|
||||||
|
@ -18,11 +19,10 @@ export default class BackgroundUploadService extends AbstractService {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Uploads a file to the server
|
* Uploads a file to the server
|
||||||
* @param listId
|
|
||||||
* @param file
|
* @param file
|
||||||
* @returns {Promise<any|never>}
|
* @returns {Promise<any|never>}
|
||||||
*/
|
*/
|
||||||
create(listId: ListModel['id'], file) {
|
create(listId: ListModel['id'], file: FileModel) {
|
||||||
return this.uploadFile(
|
return this.uploadFile(
|
||||||
this.getReplacedRoute(this.paths.create, {listId}),
|
this.getReplacedRoute(this.paths.create, {listId}),
|
||||||
file,
|
file,
|
||||||
|
|
|
@ -113,7 +113,7 @@ export default class TaskService extends AbstractService {
|
||||||
model.labels = model.labels.map(l => labelService.processModel(l))
|
model.labels = model.labels.map(l => labelService.processModel(l))
|
||||||
}
|
}
|
||||||
|
|
||||||
return model
|
return model as TaskModel
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue