Compare commits

..

4 Commits

Author SHA1 Message Date
renovate b202ca76ec chore(deps): update dev-dependencies
continuous-integration/drone/pr Build is failing Details
2024-01-15 22:25:41 +00: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
9 changed files with 147 additions and 52 deletions

View File

@ -541,6 +541,86 @@ describe('Task', () => {
.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 date = (new Date()).toLocaleString('default', {
day: '2-digit',
month: 'short',
year: 'numeric',
}) + ', 12: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 date = (new Date()).toLocaleString('default', {
day: '2-digit',
month: 'short',
year: 'numeric',
}) + ', 12: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', () => {
TaskReminderFactory.truncate()
const tasks = TaskFactory.create(1, {
@ -645,7 +725,7 @@ describe('Task', () => {
.click()
cy.get('.reminder-options-popup .card-content .reminder-period input')
.first()
.type('10')
.type('{selectall}10')
cy.get('.reminder-options-popup .card-content .reminder-period select')
.first()
.select('days')
@ -771,7 +851,7 @@ describe('Task', () => {
.should('exist')
})
it.only('Can check items off a checklist', () => {
it('Can check items off a checklist', () => {
const tasks = TaskFactory.create(1, {
id: 1,
description: `
@ -858,7 +938,7 @@ describe('Task', () => {
method: 'PUT',
url: `${Cypress.env('API_URL')}/tasks/${tasks[0].id}/attachments`,
headers: {
'Authorization': `Bearer ${window.localStorage.getItem('token')}`,
'Authorization': `Bearer ${window.localStorage.getItem('token')}`,
'Content-Type': 'multipart/form-data',
},
body: formData,

View File

@ -136,7 +136,7 @@
"@types/is-touch-device": "1.0.2",
"@types/lodash.debounce": "4.0.9",
"@types/marked": "5.0.2",
"@types/node": "20.11.2",
"@types/node": "20.11.3",
"@types/postcss-preset-env": "7.7.0",
"@types/sortablejs": "1.15.7",
"@typescript-eslint/eslint-plugin": "6.19.0",

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -256,6 +256,13 @@
--card-border-color: var(--grey-200);
--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 {
@media screen {
// Light mode colours reversed for dark mode
@ -311,6 +318,13 @@
--scheme-invert: var(--grey-900);
--scheme-invert-bis: var(--grey-900);
--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) {
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
})
},
)