Compare commits

..

8 Commits

Author SHA1 Message Date
renovate f4e258d768 fix(deps): update dependency vue to v3.4.14
continuous-integration/drone/pr Build is passing Details
2024-01-16 11:36:16 +00:00
renovate 01089f4f3d fix(deps): update tiptap to v2.1.16 (#3892)
continuous-integration/drone/push Build was killed Details
Reviewed-on: #3892
Co-authored-by: renovate <renovatebot@kolaente.de>
Co-committed-by: renovate <renovatebot@kolaente.de>
2024-01-16 11:17:28 +00:00
kolaente a7461d1ddd
chore(deps): increase renovate timeout
continuous-integration/drone/push Build is passing Details
2024-01-16 12:15:04 +01:00
kolaente a451189bb6
fix(test): make date assertion not brittle
continuous-integration/drone/push Build is passing Details
2024-01-16 10:36:29 +01:00
kolaente bf9af27fc3
fix(task): update due date when marking a task done
continuous-integration/drone/push Build is failing Details
2024-01-15 23:33:02 +01:00
kolaente 5619fda0f2
fix(task): bubble date changes from the picker up
continuous-integration/drone/push Build is failing Details
Resolves https://github.com/go-vikunja/frontend/issues/142
2024-01-15 23:23:57 +01:00
kolaente 167953b26b
fix(editor): use higher-contrast colors for links and code
continuous-integration/drone/push Build is passing Details
2024-01-15 22:11:24 +01:00
kolaente 664bf0a5f4
fix(tasks): make sure tasks show up if their parent task is not available in the current view
continuous-integration/drone/push Build is passing Details
Related https://github.com/go-vikunja/frontend/issues/136
Related https://community.vikunja.io/t/subtasks-are-hidden-when-parent-tasks-are-moved/1911
2024-01-15 21:46:47 +01:00
12 changed files with 529 additions and 435 deletions

View File

@ -541,6 +541,86 @@ describe('Task', () => {
.should('contain', 'Success') .should('contain', 'Success')
}) })
it('Can set a due date to a specific date for a task', () => {
const tasks = TaskFactory.create(1, {
id: 1,
done: false,
})
cy.visit(`/tasks/${tasks[0].id}`)
cy.get('.task-view .action-buttons .button')
.contains('Set Due Date')
.click()
cy.get('.task-view .columns.details .column')
.contains('Due Date')
.get('.date-input .datepicker .show')
.click()
cy.get('.datepicker-popup .flatpickr-innerContainer .flatpickr-days .flatpickr-day.today')
.click()
cy.get('[data-cy="closeDatepicker"]')
.contains('Confirm')
.click()
const today = new Date()
const day = today.toLocaleString('default', {day: '2-digit'})
const month = today.toLocaleString('default', {month: 'short'})
const year = today.toLocaleString('default', {year: 'numeric'})
const date = `${day} ${month} ${year}, 12:00:00`
cy.get('.task-view .columns.details .column')
.contains('Due Date')
.get('.date-input .datepicker-popup')
.should('not.exist')
cy.get('.task-view .columns.details .column')
.contains('Due Date')
.get('.date-input')
.should('contain.text', date)
cy.get('.global-notification')
.should('contain', 'Success')
})
it('Can change a due date to a specific date for a task', () => {
const dueDate = new Date()
dueDate.setHours(12)
dueDate.setMinutes(0)
dueDate.setSeconds(0)
dueDate.setDate(1)
const tasks = TaskFactory.create(1, {
id: 1,
done: false,
due_date: dueDate.toISOString(),
})
cy.visit(`/tasks/${tasks[0].id}`)
cy.get('.task-view .action-buttons .button')
.contains('Set Due Date')
.click()
cy.get('.task-view .columns.details .column')
.contains('Due Date')
.get('.date-input .datepicker .show')
.click()
cy.get('.datepicker-popup .flatpickr-innerContainer .flatpickr-days .flatpickr-day.today')
.click()
cy.get('[data-cy="closeDatepicker"]')
.contains('Confirm')
.click()
const today = new Date()
const day = today.toLocaleString('default', {day: '2-digit'})
const month = today.toLocaleString('default', {month: 'short'})
const year = today.toLocaleString('default', {year: 'numeric'})
const date = `${day} ${month} ${year}, 12:00:00`
cy.get('.task-view .columns.details .column')
.contains('Due Date')
.get('.date-input .datepicker-popup')
.should('not.exist')
cy.get('.task-view .columns.details .column')
.contains('Due Date')
.get('.date-input')
.should('contain.text', date)
cy.get('.global-notification')
.should('contain', 'Success')
})
it('Can set a reminder', () => { it('Can set a reminder', () => {
TaskReminderFactory.truncate() TaskReminderFactory.truncate()
const tasks = TaskFactory.create(1, { const tasks = TaskFactory.create(1, {
@ -645,7 +725,7 @@ describe('Task', () => {
.click() .click()
cy.get('.reminder-options-popup .card-content .reminder-period input') cy.get('.reminder-options-popup .card-content .reminder-period input')
.first() .first()
.type('10') .type('{selectall}10')
cy.get('.reminder-options-popup .card-content .reminder-period select') cy.get('.reminder-options-popup .card-content .reminder-period select')
.first() .first()
.select('days') .select('days')
@ -771,7 +851,7 @@ describe('Task', () => {
.should('exist') .should('exist')
}) })
it.only('Can check items off a checklist', () => { it('Can check items off a checklist', () => {
const tasks = TaskFactory.create(1, { const tasks = TaskFactory.create(1, {
id: 1, id: 1,
description: ` description: `
@ -858,7 +938,7 @@ describe('Task', () => {
method: 'PUT', method: 'PUT',
url: `${Cypress.env('API_URL')}/tasks/${tasks[0].id}/attachments`, url: `${Cypress.env('API_URL')}/tasks/${tasks[0].id}/attachments`,
headers: { headers: {
'Authorization': `Bearer ${window.localStorage.getItem('token')}`, 'Authorization': `Bearer ${window.localStorage.getItem('token')}`,
'Content-Type': 'multipart/form-data', 'Content-Type': 'multipart/form-data',
}, },
body: formData, body: formData,

View File

@ -55,39 +55,39 @@
"@kyvg/vue3-notification": "3.1.3", "@kyvg/vue3-notification": "3.1.3",
"@sentry/tracing": "7.93.0", "@sentry/tracing": "7.93.0",
"@sentry/vue": "7.93.0", "@sentry/vue": "7.93.0",
"@tiptap/core": "2.1.15", "@tiptap/core": "2.1.16",
"@tiptap/extension-blockquote": "2.1.15", "@tiptap/extension-blockquote": "2.1.16",
"@tiptap/extension-bold": "2.1.15", "@tiptap/extension-bold": "2.1.16",
"@tiptap/extension-bullet-list": "2.1.15", "@tiptap/extension-bullet-list": "2.1.16",
"@tiptap/extension-code": "2.1.15", "@tiptap/extension-code": "2.1.16",
"@tiptap/extension-code-block-lowlight": "2.1.15", "@tiptap/extension-code-block-lowlight": "2.1.16",
"@tiptap/extension-document": "2.1.15", "@tiptap/extension-document": "2.1.16",
"@tiptap/extension-dropcursor": "2.1.15", "@tiptap/extension-dropcursor": "2.1.16",
"@tiptap/extension-gapcursor": "2.1.15", "@tiptap/extension-gapcursor": "2.1.16",
"@tiptap/extension-hard-break": "2.1.15", "@tiptap/extension-hard-break": "2.1.16",
"@tiptap/extension-heading": "2.1.15", "@tiptap/extension-heading": "2.1.16",
"@tiptap/extension-history": "2.1.15", "@tiptap/extension-history": "2.1.16",
"@tiptap/extension-horizontal-rule": "2.1.15", "@tiptap/extension-horizontal-rule": "2.1.16",
"@tiptap/extension-image": "2.1.15", "@tiptap/extension-image": "2.1.16",
"@tiptap/extension-italic": "2.1.15", "@tiptap/extension-italic": "2.1.16",
"@tiptap/extension-link": "2.1.15", "@tiptap/extension-link": "2.1.16",
"@tiptap/extension-list-item": "2.1.15", "@tiptap/extension-list-item": "2.1.16",
"@tiptap/extension-ordered-list": "2.1.15", "@tiptap/extension-ordered-list": "2.1.16",
"@tiptap/extension-paragraph": "2.1.15", "@tiptap/extension-paragraph": "2.1.16",
"@tiptap/extension-placeholder": "2.1.15", "@tiptap/extension-placeholder": "2.1.16",
"@tiptap/extension-strike": "2.1.15", "@tiptap/extension-strike": "2.1.16",
"@tiptap/extension-table": "2.1.15", "@tiptap/extension-table": "2.1.16",
"@tiptap/extension-table-cell": "2.1.15", "@tiptap/extension-table-cell": "2.1.16",
"@tiptap/extension-table-header": "2.1.15", "@tiptap/extension-table-header": "2.1.16",
"@tiptap/extension-table-row": "2.1.15", "@tiptap/extension-table-row": "2.1.16",
"@tiptap/extension-task-item": "2.1.15", "@tiptap/extension-task-item": "2.1.16",
"@tiptap/extension-task-list": "2.1.15", "@tiptap/extension-task-list": "2.1.16",
"@tiptap/extension-text": "2.1.15", "@tiptap/extension-text": "2.1.16",
"@tiptap/extension-typography": "2.1.15", "@tiptap/extension-typography": "2.1.16",
"@tiptap/extension-underline": "2.1.15", "@tiptap/extension-underline": "2.1.16",
"@tiptap/pm": "2.1.15", "@tiptap/pm": "2.1.16",
"@tiptap/suggestion": "2.1.15", "@tiptap/suggestion": "2.1.16",
"@tiptap/vue-3": "2.1.15", "@tiptap/vue-3": "2.1.16",
"@types/is-touch-device": "1.0.2", "@types/is-touch-device": "1.0.2",
"@types/lodash.clonedeep": "4.5.9", "@types/lodash.clonedeep": "4.5.9",
"@vueuse/core": "10.7.1", "@vueuse/core": "10.7.1",

File diff suppressed because it is too large Load Diff

View File

@ -4,6 +4,11 @@
"extends": [ "extends": [
"config:js-app" "config:js-app"
], ],
"hostRules": [
{
"timeout": 600000
}
],
"packageRules": [ "packageRules": [
{ {
"matchPackageNames": ["happy-dom"], "matchPackageNames": ["happy-dom"],

View File

@ -128,9 +128,11 @@ const flatPickrDate = computed({
return return
} }
const oldDate = formatDate(date.value, 'yyy-LL-dd H:mm') if (date.value !== null) {
if (oldDate !== newValue) { const oldDate = formatDate(date.value, 'yyy-LL-dd H:mm')
return if (oldDate === newValue) {
return
}
} }
date.value = createDateFromString(newValue) date.value = createDateFromString(newValue)
updateData() updateData()

View File

@ -671,36 +671,17 @@ watch(
line-height: 1.1; line-height: 1.1;
} }
a {
color: #68cef8;
}
code { code {
background-color: rgba(#616161, 0.1); background-color: var(--grey-200);
color: #616161; color: var(--grey-700);
} }
pre { pre {
background: #0d0d0d; background: var(--grey-200);
color: #fff; color: var(--grey-700);
font-family: 'JetBrainsMono', monospace; font-family: 'JetBrainsMono', monospace;
padding: 0.75rem 1rem; padding: 0.75rem 1rem;
border-radius: 0.5rem; border-radius: $radius;
code {
color: inherit;
padding: 0;
background: none;
font-size: 0.8rem;
}
}
pre {
background: #0d0d0d;
color: #fff;
font-family: 'JetBrainsMono', monospace;
padding: 0.75rem 1rem;
border-radius: 0.5rem;
code { code {
color: inherit; color: inherit;
@ -711,7 +692,7 @@ watch(
.hljs-comment, .hljs-comment,
.hljs-quote { .hljs-quote {
color: #616161; color: var(--grey-500);
} }
.hljs-variable, .hljs-variable,
@ -724,7 +705,7 @@ watch(
.hljs-name, .hljs-name,
.hljs-selector-id, .hljs-selector-id,
.hljs-selector-class { .hljs-selector-class {
color: #f98181; color: var(--code-variable);
} }
.hljs-number, .hljs-number,
@ -734,23 +715,23 @@ watch(
.hljs-literal, .hljs-literal,
.hljs-type, .hljs-type,
.hljs-params { .hljs-params {
color: #fbbc88; color: var(--code-literal);
} }
.hljs-string, .hljs-string,
.hljs-symbol, .hljs-symbol,
.hljs-bullet { .hljs-bullet {
color: #b9f18d; color: var(--code-symbol);
} }
.hljs-title, .hljs-title,
.hljs-section { .hljs-section {
color: #faf594; color: var(--code-section);
} }
.hljs-keyword, .hljs-keyword,
.hljs-selector-tag { .hljs-selector-tag {
color: #70cff8; color: var(--code-keyword);
} }
.hljs-emphasis { .hljs-emphasis {

View File

@ -1,5 +1,5 @@
<template> <template>
<slot name="trigger" :isOpen="open" :toggle="toggle"></slot> <slot name="trigger" :isOpen="open" :toggle="toggle" :close="close"></slot>
<div <div
class="popup" class="popup"
:class="{ :class="{
@ -8,7 +8,7 @@
}" }"
ref="popup" ref="popup"
> >
<slot name="content" :isOpen="open" :toggle="toggle"/> <slot name="content" :isOpen="open" :toggle="toggle" :close="close"/>
</div> </div>
</template> </template>

View File

@ -9,7 +9,7 @@
{{ reminderText }} {{ reminderText }}
</SimpleButton> </SimpleButton>
</template> </template>
<template #content="{isOpen, toggle}"> <template #content="{isOpen, close}">
<Card class="reminder-options-popup" :class="{'is-open': isOpen}" :padding="false"> <Card class="reminder-options-popup" :class="{'is-open': isOpen}" :padding="false">
<div class="options" v-if="activeForm === null"> <div class="options" v-if="activeForm === null">
<SimpleButton <SimpleButton
@ -17,7 +17,7 @@
:key="k" :key="k"
class="option-button" class="option-button"
:class="{'currently-active': p.relativePeriod === modelValue?.relativePeriod && modelValue?.relativeTo === p.relativeTo}" :class="{'currently-active': p.relativePeriod === modelValue?.relativePeriod && modelValue?.relativeTo === p.relativeTo}"
@click="setReminderFromPreset(p, toggle)" @click="setReminderFromPreset(p, close)"
> >
{{ formatReminder(p) }} {{ formatReminder(p) }}
</SimpleButton> </SimpleButton>
@ -40,20 +40,20 @@
<ReminderPeriod <ReminderPeriod
v-if="activeForm === 'relative'" v-if="activeForm === 'relative'"
v-model="reminder" v-model="reminder"
@update:modelValue="updateDataAndMaybeClose(toggle)" @update:modelValue="updateDataAndMaybeClose(close)"
/> />
<DatepickerInline <DatepickerInline
v-if="activeForm === 'absolute'" v-if="activeForm === 'absolute'"
v-model="reminderDate" v-model="reminderDate"
@update:modelValue="setReminderDate(toggle)" @update:modelValue="setReminderDate(close)"
/> />
<x-button <x-button
v-if="showFormSwitch !== null" v-if="showFormSwitch !== null"
class="reminder__close-button" class="reminder__close-button"
:shadow="false" :shadow="false"
@click="toggle" @click="updateDataAndMaybeClose(close)"
> >
{{ $t('misc.confirm') }} {{ $t('misc.confirm') }}
</x-button> </x-button>
@ -148,25 +148,26 @@ function updateData() {
} }
} }
function setReminderDate(toggle) { function setReminderDate(close) {
reminder.value.reminder = reminderDate.value === null reminder.value.reminder = reminderDate.value === null
? null ? null
: new Date(reminderDate.value) : new Date(reminderDate.value)
reminder.value.relativeTo = null reminder.value.relativeTo = null
reminder.value.relativePeriod = 0 reminder.value.relativePeriod = 0
updateDataAndMaybeClose(toggle) updateDataAndMaybeClose(close)
} }
function setReminderFromPreset(preset, toggle) {
function setReminderFromPreset(preset, close) {
reminder.value = preset reminder.value = preset
updateData() updateData()
toggle() close()
} }
function updateDataAndMaybeClose(toggle) { function updateDataAndMaybeClose(close) {
updateData() updateData()
if (clearAfterUpdate) { if (clearAfterUpdate) {
toggle() close()
} }
} }

View File

@ -55,6 +55,7 @@ import TaskReminderModel from '@/models/taskReminder'
import type {ITaskReminder} from '@/modelTypes/ITaskReminder' import type {ITaskReminder} from '@/modelTypes/ITaskReminder'
import {REMINDER_PERIOD_RELATIVE_TO_TYPES, type IReminderPeriodRelativeTo} from '@/types/IReminderPeriodRelativeTo' import {REMINDER_PERIOD_RELATIVE_TO_TYPES, type IReminderPeriodRelativeTo} from '@/types/IReminderPeriodRelativeTo'
import {useDebounceFn} from '@vueuse/core'
const { const {
modelValue, modelValue,
@ -105,7 +106,7 @@ function updateData() {
reminder.value.relativeTo = period.value.relativeTo reminder.value.relativeTo = period.value.relativeTo
reminder.value.reminder = null reminder.value.reminder = null
emit('update:modelValue', reminder.value) useDebounceFn(() => emit('update:modelValue', reminder.value), 1000)
} }
</script> </script>

View File

@ -288,6 +288,7 @@ async function markAsDone(checked: boolean) {
title: t('task.undo'), title: t('task.undo'),
callback: () => undoDone(checked), callback: () => undoDone(checked),
}]) }])
updateDueDate()
} }
if (checked) { if (checked) {

View File

@ -256,6 +256,13 @@
--card-border-color: var(--grey-200); --card-border-color: var(--grey-200);
--logo-text-color: hsl(180, 1%, 15%); --logo-text-color: hsl(180, 1%, 15%);
// Code colors
--code-variable: #da2222;
--code-literal: #fd8a09;
--code-symbol: #0ead69;
--code-section: #3a86ff;
--code-keyword: #8338ec;
&.dark { &.dark {
@media screen { @media screen {
// Light mode colours reversed for dark mode // Light mode colours reversed for dark mode
@ -311,6 +318,13 @@
--scheme-invert: var(--grey-900); --scheme-invert: var(--grey-900);
--scheme-invert-bis: var(--grey-900); --scheme-invert-bis: var(--grey-900);
--scheme-invert-ter: var(--grey-800); --scheme-invert-ter: var(--grey-800);
// Code colors
--code-variable: #f98181;
--code-literal: #fbbc88;
--code-symbol: #b9f18d;
--code-section: #faf594;
--code-keyword: #70cff8;
} }
} }
} }

View File

@ -179,7 +179,23 @@ watch(
if (projectId < 0) { if (projectId < 0) {
return return
} }
tasks.value = tasks.value.filter(t => typeof t.relatedTasks?.parenttask === 'undefined') const tasksById = {}
tasks.value.forEach(t => tasksById[t.id] = true)
tasks.value = tasks.value.filter(t => {
if (typeof t.relatedTasks?.parenttask === 'undefined') {
return true
}
// If the task is a subtask, make sure the parent task is available in the current view as well
for (const pt of t.relatedTasks.parenttask) {
if(typeof tasksById[pt.id] === 'undefined') {
return true
}
}
return false
})
}, },
) )