Compare commits
4 Commits
7a66796b17
...
b202ca76ec
Author | SHA1 | Date | |
---|---|---|---|
b202ca76ec | |||
5619fda0f2 | |||
167953b26b | |||
664bf0a5f4 |
|
@ -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,
|
||||
|
|
18
package.json
18
package.json
|
@ -126,8 +126,8 @@
|
|||
"@cypress/vite-dev-server": "5.0.7",
|
||||
"@cypress/vue": "6.0.0",
|
||||
"@faker-js/faker": "8.3.1",
|
||||
"@histoire/plugin-screenshot": "0.17.6",
|
||||
"@histoire/plugin-vue": "0.17.6",
|
||||
"@histoire/plugin-screenshot": "0.17.8",
|
||||
"@histoire/plugin-vue": "0.17.8",
|
||||
"@rushstack/eslint-patch": "1.6.1",
|
||||
"@tsconfig/node18": "18.2.2",
|
||||
"@types/codemirror": "5.60.15",
|
||||
|
@ -136,11 +136,11 @@
|
|||
"@types/is-touch-device": "1.0.2",
|
||||
"@types/lodash.debounce": "4.0.9",
|
||||
"@types/marked": "5.0.2",
|
||||
"@types/node": "20.10.8",
|
||||
"@types/node": "20.11.3",
|
||||
"@types/postcss-preset-env": "7.7.0",
|
||||
"@types/sortablejs": "1.15.7",
|
||||
"@typescript-eslint/eslint-plugin": "6.18.1",
|
||||
"@typescript-eslint/parser": "6.18.1",
|
||||
"@typescript-eslint/eslint-plugin": "6.19.0",
|
||||
"@typescript-eslint/parser": "6.19.0",
|
||||
"@vitejs/plugin-legacy": "5.2.0",
|
||||
"@vitejs/plugin-vue": "4.6.2",
|
||||
"@vue/eslint-config-typescript": "12.0.0",
|
||||
|
@ -154,15 +154,15 @@
|
|||
"cypress": "13.6.2",
|
||||
"esbuild": "0.19.11",
|
||||
"eslint": "8.56.0",
|
||||
"eslint-plugin-vue": "9.19.2",
|
||||
"eslint-plugin-vue": "9.20.1",
|
||||
"happy-dom": "12.10.3",
|
||||
"histoire": "0.17.6",
|
||||
"histoire": "0.17.8",
|
||||
"postcss": "8.4.33",
|
||||
"postcss-easing-gradients": "3.0.1",
|
||||
"postcss-easings": "4.0.0",
|
||||
"postcss-focus-within": "8.0.1",
|
||||
"postcss-preset-env": "9.3.0",
|
||||
"rollup": "4.9.4",
|
||||
"rollup": "4.9.5",
|
||||
"rollup-plugin-visualizer": "5.12.0",
|
||||
"sass": "1.69.7",
|
||||
"start-server-and-test": "2.0.3",
|
||||
|
@ -172,7 +172,7 @@
|
|||
"vite-plugin-pwa": "0.17.4",
|
||||
"vite-plugin-sentry": "1.3.0",
|
||||
"vite-svg-loader": "5.1.0",
|
||||
"vitest": "1.1.3",
|
||||
"vitest": "1.2.0",
|
||||
"vue-tsc": "1.8.27",
|
||||
"wait-on": "7.2.0",
|
||||
"workbox-cli": "7.0.0"
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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>
|
||||
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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>
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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
|
||||
})
|
||||
},
|
||||
)
|
||||
|
||||
|
|
Reference in New Issue
Block a user