Compare commits

...

23 Commits

Author SHA1 Message Date
David Angel d0efca194f Merge branch 'main' of https://kolaente.dev/davidangel/frontend 2023-07-14 23:36:11 -04:00
renovate d19a5d9714 chore(deps): update dev-dependencies 2023-07-15 00:04:50 +00:00
renovate 90cad1c8dd chore(deps): update dev-dependencies 2023-07-14 01:09:00 +00:00
Frederick [Bot] 057017c8eb [skip ci] Updated translations via Crowdin 2023-07-14 00:08:42 +00:00
kolaente d7ce8dd320
fix(quick add magic): repeating intervals in words
Resolves vikunja/frontend#3676
2023-07-13 18:20:30 +02:00
kolaente 25b110ce48
fix(quick add magic): annually and variants spelling
Related to vikunja/frontend#3676
2023-07-13 18:05:19 +02:00
renovate 33fe5e4f20 fix(deps): update sentry-javascript monorepo to v7.58.1 2023-07-13 12:04:38 +00:00
renovate 129ef769a3 fix(deps): update sentry-javascript monorepo to v7.58.0 2023-07-13 06:47:22 +00:00
renovate 9030a9f7c1 chore(deps): update dev-dependencies 2023-07-13 02:05:04 +00:00
Frederick [Bot] 3748a496d5 [skip ci] Updated translations via Crowdin 2023-07-13 00:08:55 +00:00
renovate 890e7e1f52 chore(deps): update dependency vite to v4.4.3 2023-07-12 00:05:46 +00:00
renovate 9e0f2b0249 chore(deps): update dev-dependencies 2023-07-11 11:04:54 +00:00
renovate 9a34c522b2 fix(deps): update dependency dompurify to v3.0.5 2023-07-11 10:24:42 +00:00
renovate 60dd698fad chore(deps): update dev-dependencies to v6 2023-07-11 10:05:43 +00:00
kolaente 15ecafdf04
fix: don't try to load buckets for project id 0 2023-07-11 10:42:20 +02:00
kolaente 8902c15f7e
fix: correctly resolve kanban board in the background when moving a task
Resolves F-951
2023-07-10 18:10:14 +02:00
kolaente d5358793de
chore: provide better error messages when refreshing user info fails 2023-07-10 12:20:40 +02:00
renovate 33798b8d88 chore(deps): update pnpm to v8.6.7 2023-07-10 06:43:02 +00:00
renovate c686e8677b chore(deps): update dependency caniuse-lite to v1.0.30001514 2023-07-10 00:05:28 +00:00
renovate 5acc1696a9 fix(deps): update dependency @intlify/unplugin-vue-i18n to v0.12.2 2023-07-08 19:04:46 +00:00
renovate c4976b6a22 chore(deps): update dependency vite to v4.4.2 2023-07-08 00:06:23 +00:00
renovate d88ff594e1 fix(deps): update dependency marked to v5.1.1 2023-07-07 15:04:29 +00:00
davidangel 70ea1f2301 Merge pull request 'main' (#1) from vikunja/frontend:main into main
Reviewed-on: #1
2023-01-26 04:07:51 +00:00
9 changed files with 775 additions and 570 deletions

View File

@ -13,7 +13,7 @@
},
"homepage": "https://vikunja.io/",
"funding": "https://opencollective.com/vikunja",
"packageManager": "pnpm@8.6.6",
"packageManager": "pnpm@8.6.7",
"keywords": [
"todo",
"productivity",
@ -51,10 +51,10 @@
"@fortawesome/vue-fontawesome": "3.0.3",
"@github/hotkey": "2.0.1",
"@infectoone/vue-ganttastic": "2.1.4",
"@intlify/unplugin-vue-i18n": "0.12.1",
"@intlify/unplugin-vue-i18n": "0.12.2",
"@kyvg/vue3-notification": "2.9.1",
"@sentry/tracing": "7.57.0",
"@sentry/vue": "7.57.0",
"@sentry/tracing": "7.58.1",
"@sentry/vue": "7.58.1",
"@vueuse/core": "10.2.1",
"axios": "1.4.0",
"blurhash": "2.0.5",
@ -63,7 +63,7 @@
"codemirror": "5.65.13",
"date-fns": "2.30.0",
"dayjs": "1.11.9",
"dompurify": "3.0.4",
"dompurify": "3.0.5",
"easymde": "2.18.0",
"fast-deep-equal": "3.1.3",
"flatpickr": "4.6.13",
@ -73,7 +73,7 @@
"is-touch-device": "1.0.1",
"klona": "2.0.6",
"lodash.debounce": "4.0.8",
"marked": "5.1.0",
"marked": "5.1.1",
"pinia": "2.1.4",
"register-service-worker": "1.7.2",
"snake-case": "3.0.4",
@ -101,12 +101,12 @@
"@types/flexsearch": "0.7.3",
"@types/is-touch-device": "1.0.0",
"@types/lodash.debounce": "4.0.7",
"@types/marked": "5.0.0",
"@types/marked": "5.0.1",
"@types/node": "18.16.19",
"@types/postcss-preset-env": "7.7.0",
"@types/sortablejs": "1.15.1",
"@typescript-eslint/eslint-plugin": "5.61.0",
"@typescript-eslint/parser": "5.61.0",
"@typescript-eslint/eslint-plugin": "6.0.0",
"@typescript-eslint/parser": "6.0.0",
"@vitejs/plugin-legacy": "4.1.0",
"@vitejs/plugin-vue": "4.2.3",
"@vue/eslint-config-typescript": "11.0.3",
@ -114,16 +114,16 @@
"@vue/tsconfig": "0.4.0",
"autoprefixer": "10.4.14",
"browserslist": "4.21.9",
"caniuse-lite": "1.0.30001513",
"caniuse-lite": "1.0.30001515",
"css-has-pseudo": "6.0.0",
"csstype": "3.1.2",
"cypress": "12.17.0",
"esbuild": "0.18.11",
"eslint": "8.44.0",
"cypress": "12.17.1",
"esbuild": "0.18.12",
"eslint": "8.45.0",
"eslint-plugin-vue": "9.15.1",
"happy-dom": "10.0.3",
"happy-dom": "10.3.2",
"histoire": "0.16.2",
"postcss": "8.4.25",
"postcss": "8.4.26",
"postcss-easing-gradients": "3.0.1",
"postcss-easings": "4.0.0",
"postcss-focus-within": "8.0.0",
@ -133,13 +133,13 @@
"sass": "1.63.6",
"start-server-and-test": "2.0.0",
"typescript": "5.1.6",
"vite": "4.4.1",
"vite": "4.4.4",
"vite-plugin-inject-preload": "1.3.1",
"vite-plugin-pwa": "0.16.4",
"vite-plugin-sentry": "1.3.0",
"vite-svg-loader": "4.0.0",
"vitest": "0.33.0",
"vue-tsc": "1.8.4",
"vue-tsc": "1.8.5",
"wait-on": "7.0.1",
"workbox-cli": "7.0.0"
},

File diff suppressed because it is too large Load Diff

View File

@ -1,10 +1,12 @@
import { computed, shallowRef, watchEffect, h, type VNode } from 'vue'
import { useRoute, useRouter } from 'vue-router'
import {computed, shallowRef, watchEffect, h, type VNode} from 'vue'
import {useRoute, useRouter} from 'vue-router'
import {useBaseStore} from '@/stores/base'
export function useRouteWithModal() {
const router = useRouter()
const route = useRoute()
const backdropView = computed(() => route.fullPath && window.history.state.backdropView)
const baseStore = useBaseStore()
const routeWithModal = computed(() => {
return backdropView.value
@ -44,6 +46,18 @@ export function useRouteWithModal() {
function closeModal() {
const historyState = computed(() => route.fullPath && window.history.state)
// If the current project was changed because the user moved the currently opened task while coming from kanban,
// we need to reflect that change in the route when they close the task modal.
// The last route is only available as resolved string, therefore we need to use a regex for matching here
const kanbanRouteMatch = new RegExp('\\/projects\\/\\d+\\/kanban', 'g')
const kanbanRouter = {name: 'project.kanban', params: {projectId: baseStore.currentProject?.id}}
if (kanbanRouteMatch.test(historyState.value.back)
&& baseStore.currentProject
&& historyState.value.back !== router.resolve(kanbanRouter).fullPath) {
router.push(kanbanRouter)
return
}
if (historyState.value) {
router.back()
} else {

View File

@ -87,7 +87,7 @@
"defaultProject": "デフォルトのプロジェクト",
"timezone": "タイムゾーン",
"overdueTasksRemindersTime": "期限切れタスクのリマインダー送信時間",
"filterUsedOnOverview": "Saved filter used on the overview page"
"filterUsedOnOverview": "概要ページに使用される絞り込み条件を保存しました"
},
"totp": {
"title": "2要素認証",
@ -171,7 +171,7 @@
"title": "プロジェクト名",
"color": "色",
"projects": "プロジェクト",
"parent": "Parent Project",
"parent": "親プロジェクト",
"search": "プロジェクトのキーワードを入力…",
"searchSelect": "クリックするかEnterキーを押してプロジェクトを選択",
"shared": "共有プロジェクト",
@ -194,8 +194,8 @@
},
"background": {
"title": "プロジェクトの背景画像を設定",
"remove": "背景画像削除",
"upload": "画像の選択",
"remove": "背景画像削除",
"upload": "画像を選択…",
"searchPlaceholder": "背景画像を検索…",
"poweredByUnsplash": "Powered by Unsplash",
"loadMore": "写真をもっと読み込む",
@ -254,9 +254,9 @@
"userTeam": {
"typeUser": "ユーザー",
"typeTeam": "チーム",
"shared": "Shared with these {type}",
"you": "You",
"notShared": "Not shared with any {type} yet.",
"shared": "アクセスできる{type}",
"you": "あなた",
"notShared": "まだどの{type}とも共有していません。",
"removeHeader": "Remove a {type} from the {sharable}",
"removeText": "Are you sure you want to remove this {sharable} from the {type}? This cannot be undone!",
"removeSuccess": "The {sharable} was successfully removed from the {type}.",
@ -329,25 +329,25 @@
"title": "絞り込み",
"clear": "絞り込みの解除",
"attributes": {
"title": "Title",
"titlePlaceholder": "The saved filter title goes here…",
"title": "条件名",
"titlePlaceholder": "条件名を入力…",
"description": "説明",
"descriptionPlaceholder": "The description goes here…",
"includeNulls": "Include Tasks which don't have a value set",
"descriptionPlaceholder": "説明を入力…",
"includeNulls": "値を設定していないタスクを含める",
"requireAll": "Require all filters to be true for a task to show up",
"showDoneTasks": "完了したタスクを表示",
"sortAlphabetically": "アルファベット順に並べ替える",
"enablePriority": "優先度によるフィルターの有効化",
"enablePercentDone": "進捗状況によるフィルターの有効化",
"enablePriority": "優先度による絞り込みを有効化",
"enablePercentDone": "進捗状況による絞り込みを有効化",
"dueDateRange": "期日の範囲",
"startDateRange": "開始日の範囲",
"endDateRange": "開始日の範囲",
"reminderRange": "リマインダーの範囲"
},
"create": {
"title": "New Saved Filter",
"title": "新しい絞り込み条件の作成",
"description": "A saved filter is a virtual project which is computed from a set of filters each time it is accessed.",
"action": "Create new saved filter",
"action": "新しい絞り込み条件を作成",
"titleRequired": "Please provide a title for the filter."
},
"delete": {
@ -492,17 +492,17 @@
"ranges": {
"today": "今日",
"thisWeek": "今週",
"restOfThisWeek": "現在日時から週末まで",
"restOfThisWeek": "から週末まで",
"nextWeek": "来週",
"next7Days": "7日間",
"lastWeek": "先週",
"thisMonth": "今月",
"restOfThisMonth": "現在日時から月末まで",
"restOfThisMonth": "から月末まで",
"nextMonth": "来月",
"next30Days": "30日間",
"lastMonth": "先月",
"thisYear": "今年",
"restOfThisYear": "現在日時から年末まで"
"restOfThisYear": "から年末まで"
}
},
"datemathHelp": {
@ -618,10 +618,10 @@
},
"subscription": {
"subscribedTaskThroughParentProject": "You can't unsubscribe here because you are subscribed to this task through its project.",
"subscribedProject": "You are currently subscribed to this project and will receive notifications for changes.",
"notSubscribedProject": "You are not subscribed to this project and won't receive notifications for changes.",
"subscribedTask": "You are currently subscribed to this task and will receive notifications for changes.",
"notSubscribedTask": "You are not subscribed to this task and won't receive notifications for changes.",
"subscribedProject": "現在このプロジェクトを購読しており、変更通知が届きます。",
"notSubscribedProject": "このプロジェクトを購読していないため、変更通知は届きません。",
"subscribedTask": "現在このタスクを購読しており、変更通知が届きます。",
"notSubscribedTask": "このタスクを購読していないため、変更通知は届きません。",
"subscribe": "購読",
"unsubscribe": "購読解除",
"subscribeSuccessProject": "You are now subscribed to this project",
@ -826,34 +826,34 @@
"allPages": "これらのショートカットはすべてのページで機能します。",
"currentPageOnly": "これらのショートカットはこのページで機能します。",
"somePagesOnly": "これらのショートカットは一部のページでのみ機能します。",
"toggleMenu": "Toggle The Menu",
"toggleMenu": "メニューの表示/非表示",
"quickSearch": "Open the search/quick action bar",
"then": "then",
"then": " ",
"task": {
"title": "Task Page",
"title": "タスク",
"done": "タスクの完了/取り消し",
"assign": "Assign this task to a user",
"labels": "Add labels to this task",
"dueDate": "Change the due date of this task",
"attachment": "このタスクに添付ファイルを追加",
"related": "Modify related tasks of this task",
"color": "タスクの色変更",
"move": "Move this task to another project",
"reminder": "Manage reminders of this task",
"description": "Toggle editing of the task description",
"delete": "タスク削除",
"priority": "Change the priority of this task",
"favorite": "Mark this task as favorite / unfavorite"
"assign": "タスクに担当者を割り当てる",
"labels": "タスクにラベルを追加",
"dueDate": "タスクの期日を設定",
"attachment": "タスクに添付ファイルを追加",
"related": "関連タスクを追加",
"color": "タスクの色変更",
"move": "タスクを別のプロジェクトに移動",
"reminder": "タスクのリマインダーを設定",
"description": "タスクの説明を編集",
"delete": "タスク削除",
"priority": "タスクの優先度を設定",
"favorite": "タスクをお気に入りに追加/削除"
},
"project": {
"title": "Project Views",
"title": "プロジェクト",
"switchToListView": "リストで表示",
"switchToGanttView": "ガントチャートで表示",
"switchToKanbanView": "カンバンボードで表示",
"switchToTableView": "テーブルで表示"
},
"navigation": {
"title": "Navigation",
"title": "ナビゲーション",
"overview": "概要に移動",
"upcoming": "今後の予定に移動",
"labels": "ラベルに移動",
@ -868,13 +868,13 @@
"menu": {
"edit": "編集",
"archive": "アーカイブ",
"duplicate": "Duplicate",
"duplicate": "複製",
"delete": "削除",
"unarchive": "アーカイブの取り消し",
"setBackground": "Set background",
"setBackground": "背景画像の設定",
"share": "共有",
"newProject": "新しいプロジェクトの作成",
"createProject": "Create project"
"createProject": "プロジェクトの作成"
},
"apiConfig": {
"url": "Vikunja URL",
@ -891,9 +891,9 @@
"contact": "contact us"
},
"notification": {
"title": "Notifications",
"none": "You don't have any notifications. Have a nice day!",
"explainer": "Notifications will appear here when actions projects or tasks you subscribed to happen."
"title": "通知",
"none": "通知はありません。それではよい一日を!",
"explainer": "購読しているプロジェクトやタスクが変更されると通知されます。"
},
"quickActions": {
"commands": "コマンド",
@ -906,7 +906,7 @@
"newTask": "新しいタスク名を入力…",
"newTeam": "新しいチーム名を入力…",
"createTask": "現在のプロジェクト ({title}) にタスクを作成",
"createProject": "Create a project",
"createProject": "プロジェクトを作成",
"cmds": {
"newTask": "新しいタスクの作成",
"newProject": "新しいプロジェクトの作成",

View File

@ -13,8 +13,14 @@ export function getErrorText(r): string {
return message
}
}
let message = data?.message || r.message
if (typeof r.cause?.message !== 'undefined') {
message += ' ' + r.cause.message
}
return data?.message || r.message
return message
}
export interface Action {

View File

@ -719,15 +719,6 @@ describe('Parse Task Text', () => {
'every year': {type: 'years', amount: 1},
'every 1 year': {type: 'years', amount: 1},
'every 4 years': {type: 'years', amount: 4},
'anually': {type: 'years', amount: 1},
'bianually': {type: 'months', amount: 6},
'semiannually': {type: 'months', amount: 6},
'biennially': {type: 'years', amount: 2},
'daily': {type: 'days', amount: 1},
'hourly': {type: 'hours', amount: 1},
'monthly': {type: 'months', amount: 1},
'weekly': {type: 'weeks', amount: 1},
'yearly': {type: 'years', amount: 1},
'every one hour': {type: 'hours', amount: 1}, // maybe unnesecary but better to include it for completeness sake
'every two hours': {type: 'hours', amount: 2},
'every three hours': {type: 'hours', amount: 3},
@ -738,6 +729,15 @@ describe('Parse Task Text', () => {
'every eight hours': {type: 'hours', amount: 8},
'every nine hours': {type: 'hours', amount: 9},
'every ten hours': {type: 'hours', amount: 10},
'annually': {type: 'years', amount: 1},
'biannually': {type: 'months', amount: 6},
'semiannually': {type: 'months', amount: 6},
'biennially': {type: 'years', amount: 2},
'daily': {type: 'days', amount: 1},
'hourly': {type: 'hours', amount: 1},
'monthly': {type: 'months', amount: 1},
'weekly': {type: 'weeks', amount: 1},
'yearly': {type: 'years', amount: 1},
} as Record<string, IRepeatAfter>
for (const c in cases) {
@ -749,5 +749,26 @@ describe('Parse Task Text', () => {
expect(result?.repeats?.amount).toBe(cases[c].amount)
})
}
const wordCases = [
'annually',
'biannually',
'semiannually',
'biennially',
'daily',
'hourly',
'monthly',
'weekly',
'yearly',
]
wordCases.forEach(c => {
it(`should ignore recurring periods if they are part of a word ${c}`, () => {
const result = parseTaskText(`Lorem Ipsum word${c}notword`)
expect(result.text).toBe(`Lorem Ipsum word${c}notword`)
expect(result?.repeats).toBeNull()
})
})
})
})

View File

@ -165,7 +165,7 @@ const getPriority = (text: string, prefix: string): number | null => {
}
const getRepeats = (text: string): repeatParsedResult => {
const regex = /((every|each) (([0-9]+|one|two|three|four|five|six|seven|eight|nine|ten) )?(hours?|days?|weeks?|months?|years?))|anually|bianually|semiannually|biennially|daily|hourly|monthly|weekly|yearly/ig
const regex = /(^| )(((every|each) (([0-9]+|one|two|three|four|five|six|seven|eight|nine|ten) )?(hours?|days?|weeks?|months?|years?))|(annually|biannually|semiannually|biennially|daily|hourly|monthly|weekly|yearly))($| )/ig
const results = regex.exec(text)
if (results === null) {
return {
@ -175,7 +175,7 @@ const getRepeats = (text: string): repeatParsedResult => {
}
let amount = 1
switch (results[3] ? results[3].trim() : undefined) {
switch (results[5] ? results[5].trim() : undefined) {
case 'one':
amount = 1
break
@ -207,22 +207,22 @@ const getRepeats = (text: string): repeatParsedResult => {
amount = 10
break
default:
amount = results[3] ? parseInt(results[3]) : 1
amount = results[5] ? parseInt(results[5]) : 1
}
let type: IRepeatType = REPEAT_TYPES.Hours
switch (results[0]) {
switch (results[2]) {
case 'biennially':
type = REPEAT_TYPES.Years
amount = 2
break
case 'bianually':
case 'biannually':
case 'semiannually':
type = REPEAT_TYPES.Months
amount = 6
break
case 'yearly':
case 'anually':
case 'annually':
type = REPEAT_TYPES.Years
break
case 'daily':
@ -238,7 +238,7 @@ const getRepeats = (text: string): repeatParsedResult => {
type = REPEAT_TYPES.Weeks
break
default:
switch (results[5]) {
switch (results[7]) {
case 'hour':
case 'hours':
type = REPEAT_TYPES.Hours

View File

@ -296,7 +296,16 @@ export const useAuthStore = defineStore('auth', () => {
logout()
return
}
throw new Error('Error while refreshing user info:', {cause: e})
const cause = {e}
if (typeof e?.response?.data?.message !== 'undefined') {
cause.message = e.response.data.message
}
console.error('Error refreshing user info:', e)
throw new Error('Error while refreshing user info:', {cause})
}
}

View File

@ -1,7 +1,7 @@
<template>
<ProjectWrapper
class="project-kanban"
:project-id="projectId"
:project-id="project.id"
viewName="kanban"
>
<template #header>
@ -224,7 +224,7 @@
</template>
<script setup lang="ts">
import {computed, nextTick, ref, watch, type PropType} from 'vue'
import {computed, nextTick, ref, watch} from 'vue'
import {useI18n} from 'vue-i18n'
import draggable from 'zhyswan-vuedraggable'
import {klona} from 'klona/lite'
@ -263,13 +263,6 @@ const DRAG_OPTIONS = {
const MIN_SCROLL_HEIGHT_PERCENT = 0.25
const props = defineProps({
projectId: {
type: Number as PropType<IProject['id']>,
required: true,
},
})
const {t} = useI18n({useScope: 'global'})
const baseStore = useBaseStore()
@ -340,11 +333,12 @@ const taskLoading = computed(() => taskStore.isLoading)
watch(
() => ({
projectId: props.projectId,
params: params.value,
project: project.value,
}),
({projectId, params}) => {
if (projectId === undefined) {
({params, project}) => {
const projectId = project.id
if (projectId === undefined || Number(projectId) === 0) {
return
}
collapsedBuckets.value = getCollapsedBucketState(projectId)
@ -483,7 +477,7 @@ async function addTaskToBucket(bucketId: IBucket['id']) {
const task = await taskStore.createNewTask({
title: newTaskText.value,
bucketId,
projectId: props.projectId,
projectId: project.value.id,
})
newTaskText.value = ''
kanbanStore.addTaskToBucket(task)
@ -505,7 +499,7 @@ async function createNewBucket() {
await kanbanStore.createBucket(new BucketModel({
title: newBucketTitle.value,
projectId: props.projectId,
projectId: project.value.id,
}))
newBucketTitle.value = ''
showNewBucketInput.value = false
@ -525,7 +519,7 @@ async function deleteBucket() {
await kanbanStore.deleteBucket({
bucket: new BucketModel({
id: bucketToDelete.value,
projectId: props.projectId,
projectId: project.value.id,
}),
params: params.value,
})
@ -612,7 +606,7 @@ async function toggleDoneBucket(bucket: IBucket) {
function collapseBucket(bucket: IBucket) {
collapsedBuckets.value[bucket.id] = true
saveCollapsedBucketState(props.projectId, collapsedBuckets.value)
saveCollapsedBucketState(project.value.id, collapsedBuckets.value)
}
function unCollapseBucket(bucket: IBucket) {
@ -621,7 +615,7 @@ function unCollapseBucket(bucket: IBucket) {
}
collapsedBuckets.value[bucket.id] = false
saveCollapsedBucketState(props.projectId, collapsedBuckets.value)
saveCollapsedBucketState(project.value.id, collapsedBuckets.value)
}
</script>