Compare commits

..

57 Commits

Author SHA1 Message Date
2cf1e91b63
fix: passing readonly projects data to navigation
Some checks failed
continuous-integration/drone/pr Build is failing
2023-04-01 11:23:08 +02:00
9fd6ea2f4d
chore: move loader class
Some checks failed
continuous-integration/drone/pr Build is failing
2023-04-01 11:20:28 +02:00
08e2a44b20
chore: export favorite projects from store
Some checks failed
continuous-integration/drone/pr Build is failing
2023-04-01 11:18:11 +02:00
e3a4f87988
chore: remove unnecessary map
Some checks failed
continuous-integration/drone/pr Build is failing
2023-04-01 11:15:28 +02:00
01aee0a64b
chore: export not archived root projects 2023-04-01 11:15:12 +02:00
baed501967
fix: show favorite on hover 2023-04-01 11:07:38 +02:00
1f69763c3d
fix: don't show > for top-level projects
Some checks failed
continuous-integration/drone/pr Build is failing
2023-03-29 17:12:59 +02:00
ecb8862872
feat: allow selecting a parent project when editing a project
Some checks failed
continuous-integration/drone/pr Build is failing
2023-03-29 17:10:21 +02:00
6d4c746586
feat: allow selecting a parent project when creating a project
Some checks failed
continuous-integration/drone/pr Build is failing
2023-03-29 17:05:09 +02:00
56b607ef8e
feat: allow selecting a parent project when duplicating a project
Some checks failed
continuous-integration/drone/pr Build is failing
2023-03-29 16:19:58 +02:00
2557b182dd
feat: don't handle child projects and instead only save the ids
Some checks failed
continuous-integration/drone/pr Build is failing
2023-03-29 16:09:18 +02:00
0d0b3c0ca7
fix: make computed side-effect free
Some checks failed
continuous-integration/drone/pr Build is failing
2023-03-28 21:31:38 +02:00
b9c18b8f8b
chore: refactor get parents project and move to projects store
Some checks failed
continuous-integration/drone/pr Build is failing
2023-03-28 21:28:29 +02:00
1322d49624
feat: show all parent projects in project search
Some checks failed
continuous-integration/drone/pr Build is failing
2023-03-28 18:21:12 +02:00
0c5589859d
feat: show all parent projects in task detail view
Some checks failed
continuous-integration/drone/pr Build is failing
2023-03-28 17:48:26 +02:00
4d73a8abfc
fix: add await
Some checks failed
continuous-integration/drone/pr Build is failing
2023-03-28 17:25:34 +02:00
0405b97040
fix(filters): load projects after updating a filter
Some checks failed
continuous-integration/drone/pr Build is failing
2023-03-28 16:45:08 +02:00
c4d13dff2c
fix(filters): load projects after deleting a filter
Some checks failed
continuous-integration/drone/pr Build is failing
2023-03-28 16:44:48 +02:00
fda2e41346
fix(filters): load projects after creating a filter
Some checks failed
continuous-integration/drone/pr Build is failing
2023-03-28 16:44:20 +02:00
a0d45dcd89
chore(task): move toggleFavorite to store
Some checks failed
continuous-integration/drone/pr Build is failing
2023-03-28 16:31:33 +02:00
ebb239f683
feat(projects): move hasProjects check to store
Some checks failed
continuous-integration/drone/pr Build is failing
2023-03-28 16:27:07 +02:00
b1cb26d423
feat: wrap projects navigation in a <Suspense> so that we can use top level await
Some checks failed
continuous-integration/drone/pr Build is failing
2023-03-28 16:18:53 +02:00
92c4868511
chore: use long variable name
Some checks failed
continuous-integration/drone/pr Build is failing
2023-03-28 15:42:02 +02:00
3368a449c2
chore: rename archived message key
Some checks failed
continuous-integration/drone/pr Build is failing
2023-03-28 15:40:08 +02:00
9633e169c1
fix: use correct shortcut to open projects overview
Some checks failed
continuous-integration/drone/pr Build is failing
2023-03-28 15:38:56 +02:00
82a47f4d89
fix: simplify sort
Some checks failed
continuous-integration/drone/pr Build is failing
2023-03-28 15:37:35 +02:00
51f2ffc2c2
chore: export projects as array directly from projects store
Some checks failed
continuous-integration/drone/pr Build is failing
2023-03-28 15:36:31 +02:00
39b076ef6b
chore: rename prop
Some checks failed
continuous-integration/drone/pr Build is failing
2023-03-28 15:33:34 +02:00
71423f25c0
feat(tests): add project tests derived from old namespace tests
Some checks failed
continuous-integration/drone/pr Build is failing
2023-03-28 15:30:20 +02:00
ab57dd59f0
fix(projects): make sure the project hierarchy is properly updated when moving projects between parents
Some checks failed
continuous-integration/drone/pr Build is failing
2023-03-28 14:44:07 +02:00
73f484db44
feat(navigation): show favorite projects on top 2023-03-28 14:29:15 +02:00
c7d78daa91
fix(navigation): make sure updating a project's state works for sub projects as well. 2023-03-28 14:21:26 +02:00
b72a1d9d0c
fix(navigation): make marking a project as favorite work 2023-03-28 14:07:13 +02:00
600c148bb1
fix(navigation): make sure the Favorites project shows up when marking or unmarking a task as favorite 2023-03-28 14:03:41 +02:00
fde8a17d01
fix(navigation): favorites project 2023-03-28 13:58:11 +02:00
52f340412f
fix(task detail view): make project display show the task's project 2023-03-27 14:26:39 +02:00
66bf2d7e14
fix: make check if projects are available work again 2023-03-27 14:26:11 +02:00
1baf20471a
fix: cleanup unused translation strings 2023-03-27 14:25:48 +02:00
1d6c760c88
fix: make tests work again 2023-03-27 14:25:31 +02:00
05520a58ce
chore: cleanup namespace leftovers 2023-03-27 13:34:32 +02:00
0513254f9e
fix(navigation): hide left ul border 2023-03-27 13:28:44 +02:00
13ae47e3fd
feat(navigation): make dragging a project under another project work 2023-03-27 13:25:23 +02:00
b623e739de
feat(navigation): allow dragging a project out from its parent project 2023-03-27 13:22:03 +02:00
713edd4dfb
feat(navigation): make dragging a project to a parent work 2023-03-27 13:21:47 +02:00
2cd8861e9b
fix(navigation): hover state of other menu items 2023-03-27 11:58:27 +02:00
16bde80411
feat(navigation): add hiding child projects 2023-03-27 11:33:24 +02:00
b937a39b21
feat: translate inbox project title 2023-03-27 11:08:25 +02:00
cff26b1ccc
chore: format 2023-03-27 11:05:55 +02:00
f3b44a2f1c
feat(navigation): correctly show child projects 2023-03-27 11:05:22 +02:00
2952feec0b
fix(navigation): make the styles work again 2023-03-27 10:26:34 +02:00
875cf619a2
fix(navigation): watcher 2023-03-27 10:26:02 +02:00
b1e6f5ccd6
feat: rebuild main navigation so that it works recursively with projects 2023-03-25 18:10:41 +01:00
1e79a982fb
fix: remove namespace routes 2023-03-25 14:55:11 +01:00
23e8a07dcf
fix: remove namespace store reference 2023-03-25 14:55:03 +01:00
9925586a19
feat: remove all namespace leftovers 2023-03-25 14:54:20 +01:00
1a05e1a4a2
fix: route to create new project 2023-03-25 14:29:00 +01:00
61cbf3ccab
feat: move namespaces list to projects list 2023-03-25 14:27:19 +01:00
22 changed files with 3411 additions and 4250 deletions

14
.npmrc
View File

@ -1,14 +1,2 @@
fetch-timeout=100000
# pnpm settings
# The following settings prepare for the new default value of pnpm 8
# they can be removed directly after having moved to pnpm 8
auto-install-peers=true
dedupe-peer-dependents=true
resolve-peers-from-workspace-root=true
save-workspace-protocol=rolling
resolution-mode=lowest-direct
publishConfig.linkDirectory=true
# remove some time after having moved to pnpm 8
use-lockfile-v6=true
fetch-timeout=100000

View File

@ -22,10 +22,10 @@ describe('Project View Table', () => {
cy.get('.project-table .filter-container .items .button')
.contains('Columns')
.click()
cy.get('.project-table .filter-container .card.columns-filter .card-content .fancycheckbox')
cy.get('.project-table .filter-container .card.columns-filter .card-content .fancycheckbox .check')
.contains('Priority')
.click()
cy.get('.project-table .filter-container .card.columns-filter .card-content .fancycheckbox')
cy.get('.project-table .filter-container .card.columns-filter .card-content .fancycheckbox .check')
.contains('Done')
.click()

View File

@ -93,7 +93,7 @@ describe('Task', () => {
TaskFactory.create(1)
cy.visit('/projects/1/list')
cy.get('.tasks .task .fancycheckbox')
cy.get('.tasks .task .fancycheckbox label.check')
.first()
.click()
cy.get('.global-notification')

10
env.d.ts vendored
View File

@ -3,16 +3,6 @@
/// <reference types="cypress" />
/// <reference types="@histoire/plugin-vue/components" />
declare module 'postcss-focus-within/browser' {
import focusWithinInit from 'postcss-focus-within/browser'
export default focusWithinInit
}
declare module 'css-has-pseudo/browser' {
import cssHasPseudo from 'css-has-pseudo/browser'
export default cssHasPseudo
}
interface ImportMetaEnv {
readonly VITE_IS_ONLINE: boolean
}

View File

@ -2,11 +2,11 @@
"nodes": {
"nixpkgs": {
"locked": {
"lastModified": 1680030621,
"narHash": "sha256-qQa1NeS5Rvk2lgK5lSk986PC6I72yIHejzM8PFu+dHs=",
"lastModified": 1664753041,
"narHash": "sha256-0ogaD8PaGHluARFeupofvk1Nq9gpVeZdlFM0Kcwguys=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "402cc3633cc60dfc50378197305c984518b30773",
"rev": "a62844b302507c7531ad68a86cb7aa54704c9cb4",
"type": "github"
},
"original": {

View File

@ -13,7 +13,7 @@
},
"homepage": "https://vikunja.io/",
"funding": "https://opencollective.com/vikunja",
"packageManager": "pnpm@7.30.5",
"packageManager": "pnpm@7.30.3",
"keywords": [
"todo",
"productivity",
@ -45,16 +45,19 @@
"story:preview": "histoire preview"
},
"dependencies": {
"@fortawesome/fontawesome-svg-core": "6.4.0",
"@fortawesome/free-regular-svg-icons": "6.4.0",
"@fortawesome/free-solid-svg-icons": "6.4.0",
"@fortawesome/fontawesome-svg-core": "6.3.0",
"@fortawesome/free-regular-svg-icons": "6.3.0",
"@fortawesome/free-solid-svg-icons": "6.3.0",
"@fortawesome/vue-fontawesome": "3.0.3",
"@github/hotkey": "2.0.1",
"@infectoone/vue-ganttastic": "2.1.4",
"@intlify/unplugin-vue-i18n": "0.10.0",
"@kyvg/vue3-notification": "2.9.0",
"@sentry/tracing": "7.46.0",
"@sentry/vue": "7.46.0",
"@sentry/tracing": "7.45.0",
"@sentry/vue": "7.45.0",
"@types/is-touch-device": "1.0.0",
"@types/lodash.clonedeep": "4.5.7",
"@types/sortablejs": "1.15.1",
"@vueuse/core": "9.13.0",
"axios": "1.3.4",
"blurhash": "2.0.5",
@ -69,6 +72,7 @@
"flatpickr": "4.6.13",
"flexsearch": "0.7.31",
"floating-vue": "2.0.0-beta.20",
"focus-within": "3.0.2",
"highlight.js": "11.7.0",
"is-touch-device": "1.0.1",
"klona": "2.0.6",
@ -98,14 +102,13 @@
"@types/codemirror": "5.60.7",
"@types/dompurify": "3.0.0",
"@types/flexsearch": "0.7.3",
"@types/is-touch-device": "1.0.0",
"@types/focus-within": "1.0.1",
"@types/lodash.debounce": "4.0.7",
"@types/marked": "4.0.8",
"@types/node": "18.15.11",
"@types/node": "18.15.9",
"@types/postcss-preset-env": "7.7.0",
"@types/sortablejs": "1.15.1",
"@typescript-eslint/eslint-plugin": "5.57.0",
"@typescript-eslint/parser": "5.57.0",
"@typescript-eslint/eslint-plugin": "5.56.0",
"@typescript-eslint/parser": "5.56.0",
"@vitejs/plugin-legacy": "4.0.2",
"@vitejs/plugin-vue": "4.1.0",
"@vue/eslint-config-typescript": "11.0.2",
@ -113,31 +116,29 @@
"@vue/tsconfig": "0.1.3",
"autoprefixer": "10.4.14",
"browserslist": "4.21.5",
"caniuse-lite": "1.0.30001470",
"css-has-pseudo": "5.0.2",
"caniuse-lite": "1.0.30001468",
"csstype": "3.1.1",
"cypress": "12.9.0",
"esbuild": "0.17.14",
"eslint": "8.37.0",
"cypress": "12.8.1",
"esbuild": "0.17.13",
"eslint": "8.36.0",
"eslint-plugin-vue": "9.10.0",
"happy-dom": "8.9.0",
"histoire": "0.15.9",
"netlify-cli": "13.2.1",
"netlify-cli": "13.1.6",
"postcss": "8.4.21",
"postcss-easing-gradients": "3.0.1",
"postcss-easings": "3.0.1",
"postcss-focus-within": "7.0.2",
"postcss-preset-env": "8.3.0",
"postcss-preset-env": "8.1.0",
"rollup": "3.20.2",
"rollup-plugin-visualizer": "5.9.0",
"sass": "1.60.0",
"start-server-and-test": "2.0.0",
"typescript": "5.0.3",
"typescript": "5.0.2",
"vite": "4.2.1",
"vite-plugin-inject-preload": "1.3.1",
"vite-plugin-pwa": "0.14.7",
"vite-plugin-pwa": "0.14.6",
"vite-svg-loader": "4.0.0",
"vitest": "0.29.8",
"vitest": "0.29.7",
"vue-tsc": "1.2.0",
"wait-on": "7.0.1",
"workbox-cli": "6.5.4"

File diff suppressed because it is too large Load Diff

View File

@ -1,4 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" width="18px" height="18px" viewBox="0 0 18 18" fill="none" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5">
<path d="M1,9 L1,3.5 C1,2 2,1 3.5,1 L14.5,1 C16,1 17,2 17,3.5 L17,14.5 C17,16 16,17 14.5,17 L3.5,17 C2,17 1,16 1,14.5 L1,9 Z" stroke-dasharray="60"></path>
<polyline points="1 9 7 14 15 4" stroke-dasharray="22" stroke-dashoffset="66"></polyline>
</svg>

Before

Width:  |  Height:  |  Size: 420 B

View File

@ -1,54 +0,0 @@
<template>
<div class="base-checkbox" v-cy="'checkbox'">
<input
type="checkbox"
:id="checkboxId"
class="is-sr-only"
:checked="modelValue"
@change="(event) => emit('update:modelValue', (event.target as HTMLInputElement).checked)"
:disabled="disabled || undefined"
/>
<slot name="label" :checkboxId="checkboxId">
<label :for="checkboxId" class="base-checkbox__label">
<slot/>
</label>
</slot>
</div>
</template>
<script setup lang="ts">
import {ref} from 'vue'
import {createRandomID} from '@/helpers/randomId'
defineProps({
modelValue: {
type: Boolean,
default: false,
},
disabled: {
type: Boolean,
default: false,
},
})
const emit = defineEmits<{
(event: 'update:modelValue', value: boolean): void
}>()
const checkboxId = ref(`fancycheckbox_${createRandomID()}`)
</script>
<style lang="scss" scoped>
.base-checkbox__label {
cursor: pointer;
user-select: none;
-webkit-tap-highlight-color: transparent;
display: inline-flex;
}
.base-checkbox:has(input:disabled) .base-checkbox__label {
cursor:not-allowed;
pointer-events: none;
}
</style>

View File

@ -1,71 +0,0 @@
<script lang="ts" setup>
import {ref} from 'vue'
import {logEvent} from 'histoire/client'
import FancyCheckbox from './fancycheckbox.vue'
const isDisabled = ref<boolean | undefined>()
const isChecked = ref(false)
const isCheckedInitiallyEnabled = ref(true)
const isCheckedDisabled = ref(false)
const withoutInitialState = ref<boolean | undefined>()
</script>
<template>
<Story :layout="{ type: 'grid', width: '200px' }">
<Variant title="Default">
<FancyCheckbox
v-model="isChecked"
:disabled="isDisabled"
>
This is probably not important
</FancyCheckbox>
Visualisation
<input type="checkbox" v-model="isChecked">
{{ isChecked }}
</Variant>
<Variant title="Enabled Initially">
<FancyCheckbox
:disabled="isDisabled"
v-model="isCheckedInitiallyEnabled"
>
We want you to use this option
</FancyCheckbox>
Visualisation
<input type="checkbox" v-model="isCheckedInitiallyEnabled">
{{ isCheckedInitiallyEnabled }}
</Variant>
<Variant title="Disabled">
<FancyCheckbox
disabled
:modelValue="isCheckedDisabled"
@update:model-value="logEvent('Setting disabled: This should never happen', $event)"
>
You can't change this
</FancyCheckbox>
Visualisation
<input type="checkbox" v-model="isCheckedDisabled" disabled>
{{ isCheckedDisabled }}
</Variant>
<Variant title="Undefined initial State">
<FancyCheckbox
v-model="withoutInitialState"
:disabled="isDisabled"
>
Not sure what the value should be
</FancyCheckbox>
Visualisation
<input type="checkbox" v-model="withoutInitialState" disabled>
{{ withoutInitialState }}
</Variant>
</Story>
</template>

View File

@ -1,42 +1,66 @@
<template>
<BaseCheckbox
class="fancycheckbox"
:class="{
'is-disabled': disabled,
'is-block': isBlock,
}"
:disabled="disabled"
:model-value="modelValue"
@update:model-value="value => emit('update:modelValue', value)"
>
<CheckboxIcon class="fancycheckbox__icon" />
<span v-if="$slots.default" class="fancycheckbox__content">
<slot/>
</span>
</BaseCheckbox>
<div :class="{'is-disabled': disabled}" class="fancycheckbox">
<input
:checked="checked"
:disabled="disabled || undefined"
:id="checkBoxId"
@change="(event: Event) => updateData((event.target as HTMLInputElement).checked)"
type="checkbox"
/>
<label :for="checkBoxId" class="check" @click.prevent="check">
<svg height="18px" viewBox="0 0 18 18" width="18px">
<path
d="M1,9 L1,3.5 C1,2 2,1 3.5,1 L14.5,1 C16,1 17,2 17,3.5 L17,14.5 C17,16 16,17 14.5,17 L3.5,17 C2,17 1,16 1,14.5 L1,9 Z"></path>
<polyline points="1 9 7 14 15 4"></polyline>
</svg>
<span>
<slot></slot>
</span>
</label>
</div>
</template>
<script setup lang="ts">
import CheckboxIcon from '@/assets/checkbox.svg?component'
import {ref, toRef, watch} from 'vue'
import BaseCheckbox from '@/components/base/BaseCheckbox.vue'
import {createRandomID} from '@/helpers/randomId'
defineProps({
const checked = ref(false)
const checkBoxId = `fancycheckbox_${createRandomID()}`
const props = defineProps({
modelValue: {
type: Boolean,
required: false,
},
disabled: {
type: Boolean,
},
isBlock: {
type: Boolean,
required: false,
default: false,
},
})
const emit = defineEmits(['update:modelValue', 'change'])
const emit = defineEmits<{
(event: 'update:modelValue', value: boolean): void
}>()
const modelValue = toRef(props, 'modelValue')
watch(
modelValue,
newValue => {
checked.value = newValue
},
{immediate: true},
)
function updateData(newChecked: boolean) {
checked.value = newChecked
emit('update:modelValue', newChecked)
emit('change', newChecked)
}
function check() {
checked.value = !checked.value
updateData(checked.value)
}
</script>
@ -46,54 +70,75 @@ const emit = defineEmits<{
padding-right: 5px;
padding-top: 3px;
// FIXME: should be a prop
&.is-block {
display: block;
margin: .5rem .2rem;
}
}
.fancycheckbox__content {
input[type=checkbox] {
display: none;
}
.check {
cursor: pointer;
position: relative;
margin: auto;
width: 18px;
height: 18px;
-webkit-tap-highlight-color: transparent;
transform: translate3d(0, 0, 0);
}
span {
font-size: 0.8rem;
vertical-align: top;
padding-left: .5rem;
}
.fancycheckbox__icon:deep() {
svg {
position: relative;
z-index: 1;
stroke: var(--stroke-color, #c8ccd4);
fill: none;
stroke-linecap: round;
stroke-linejoin: round;
stroke: #c8ccd4;
stroke-width: 1.5;
transform: translate3d(0, 0, 0);
transition: all 0.2s ease;
}
path,
polyline {
transition: all 0.2s linear, color 0.2s ease;
.check:hover svg {
stroke: var(--primary);
}
.is-disabled .check:hover svg {
stroke: #c8ccd4;
}
path {
stroke-dasharray: 60;
stroke-dashoffset: 0;
}
polyline {
stroke-dasharray: 22;
stroke-dashoffset: 66;
}
input[type=checkbox]:checked + .check {
svg {
stroke: var(--primary);
}
}
.fancycheckbox:not(:has(input:disabled)):hover .fancycheckbox__icon,
.fancycheckbox:has(input:checked) .fancycheckbox__icon {
--stroke-color: var(--primary);
}
</style>
<style lang="scss">
// Since css-has-pseudo doesn't work with deep classes,
// the following rules can't be scoped
.fancycheckbox:has(:not(input:checked)) .fancycheckbox__icon {
path {
transition-delay: 0.05s;
}
}
.fancycheckbox:has(input:checked) .fancycheckbox__icon {
path {
stroke-dashoffset: 60;
transition: all 0.3s linear;
}
polyline {
stroke-dashoffset: 42;
transition: all 0.2s linear;
transition-delay: 0.15s;
}
}

View File

@ -1,8 +1,12 @@
<template>
<div :class="{'is-loading': taskService.loading}" class="task loader-container">
<router-link
:to="taskDetailRoute"
:class="{'is-loading': taskService.loading}"
class="task loader-container"
>
<fancycheckbox
:disabled="(isArchived || disabled) && !canMarkAsDone"
@update:model-value="markAsDone"
@change="markAsDone"
v-model="task.done"
/>
@ -12,8 +16,7 @@
class="mr-1"
/>
<router-link
:to="taskDetailRoute"
<div
:class="{ 'done': task.done, 'show-project': showProject && project !== null}"
class="tasktext"
>
@ -93,7 +96,7 @@
</span>
<checklist-summary :task="task"/>
</router-link>
</div>
<progress
class="progress is-small"
@ -114,14 +117,14 @@
<BaseButton
:class="{'is-favorite': task.isFavorite}"
@click="toggleFavorite"
@click.prevent="toggleFavorite"
class="favorite"
>
<icon icon="star" v-if="task.isFavorite"/>
<icon :icon="['far', 'star']" v-else/>
</BaseButton>
<slot />
</div>
</router-link>
</template>
<script setup lang="ts">
@ -281,7 +284,11 @@ function hideDeferDueDatePopup(e) {
border-radius: $radius;
border: 2px solid transparent;
color: var(--text);
transition: color ease $transition-duration;
&:hover {
color: var(--grey-900);
background-color: var(--grey-100);
}
@ -327,15 +334,6 @@ function hideDeferDueDatePopup(e) {
}
a {
color: var(--text);
transition: color ease $transition-duration;
&:hover {
color: var(--grey-900);
}
}
.favorite {
opacity: 1;
text-align: center;

View File

@ -6,11 +6,13 @@ declare global {
}
}
const cypressDirective = <Directive<HTMLElement,string>>{
mounted(el, {arg, value}) {
const testingId = arg || value
if ((window.Cypress || import.meta.env.DEV) && testingId) {
el.setAttribute('data-cy', testingId)
const cypressDirective: Directive = {
mounted(el, {value}) {
if (
(window.Cypress || import.meta.env.DEV) &&
value
) {
el.setAttribute('data-cy', value)
}
},
beforeUnmount(el) {

View File

@ -6,16 +6,16 @@ export const ERROR_NO_API_URL = 'noApiUrlProvided'
export const checkAndSetApiUrl = (url: string): Promise<string> => {
if (url.startsWith('/')) {
if(url.startsWith('/')) {
url = window.location.host + url
}
// Check if the url has a http prefix
// Check if the url has an http prefix
if (
!url.startsWith('http://') &&
!url.startsWith('https://')
) {
url = `${window.location.protocol}//${url}`
url = `http://${url}`
}
const urlToCheck: URL = new URL(url)
@ -41,6 +41,15 @@ export const checkAndSetApiUrl = (url: string): Promise<string> => {
}
throw e
})
.catch(e => {
// Check if it has a port and if not check if it is reachable at https
if (urlToCheck.protocol === 'http:') {
urlToCheck.protocol = 'https:'
window.API_URL = urlToCheck.toString()
return updateConfig()
}
throw e
})
.catch(e => {
// Check if it is reachable at /api/v1 and https
urlToCheck.pathname = origUrlToCheck.pathname
@ -57,6 +66,7 @@ export const checkAndSetApiUrl = (url: string): Promise<string> => {
.catch(e => {
// Check if it is reachable at port API_DEFAULT_PORT and https
if (urlToCheck.port !== API_DEFAULT_PORT) {
urlToCheck.protocol = 'https:'
urlToCheck.port = API_DEFAULT_PORT
window.API_URL = urlToCheck.toString()
return updateConfig()
@ -64,7 +74,30 @@ export const checkAndSetApiUrl = (url: string): Promise<string> => {
throw e
})
.catch(e => {
// Check if it is reachable at :API_DEFAULT_PORT and /api/v1
// Check if it is reachable at :API_DEFAULT_PORT and /api/v1 and https
urlToCheck.pathname = origUrlToCheck.pathname
if (
!urlToCheck.pathname.endsWith('/api/v1') &&
!urlToCheck.pathname.endsWith('/api/v1/')
) {
urlToCheck.pathname = `${urlToCheck.pathname}api/v1`
window.API_URL = urlToCheck.toString()
return updateConfig()
}
throw e
})
.catch(e => {
// Check if it is reachable at port API_DEFAULT_PORT and http
if (urlToCheck.port !== API_DEFAULT_PORT) {
urlToCheck.protocol = 'http:'
urlToCheck.port = API_DEFAULT_PORT
window.API_URL = urlToCheck.toString()
return updateConfig()
}
throw e
})
.catch(e => {
// Check if it is reachable at :API_DEFAULT_PORT and /api/v1 and http
urlToCheck.pathname = origUrlToCheck.pathname
if (
!urlToCheck.pathname.endsWith('/api/v1') &&
@ -85,7 +118,7 @@ export const checkAndSetApiUrl = (url: string): Promise<string> => {
localStorage.setItem('API_URL', window.API_URL)
return window.API_URL
}
throw new Error(ERROR_NO_API_URL)
})
}

View File

@ -1,5 +1,4 @@
import './polyfills'
import {defineSetupVue3} from '@histoire/plugin-vue'
import { defineSetupVue3 } from '@histoire/plugin-vue'
import {i18n} from './i18n'
// import './histoire.css' // Import global CSS
@ -7,21 +6,18 @@ import './styles/global.scss'
import {createPinia} from 'pinia'
import cypress from '@/directives/cypress'
import FontAwesomeIcon from '@/components/misc/Icon'
import XButton from '@/components/input/button.vue'
import Modal from '@/components/misc/modal.vue'
import Card from '@/components/misc/card.vue'
export const setupVue3 = defineSetupVue3(({ app }) => {
// Add Pinia store
const pinia = createPinia()
app.use(pinia)
app.use(i18n)
app.directive('cy', cypress)
app.component('icon', FontAwesomeIcon)
app.component('XButton', XButton)
app.component('modal', Modal)

View File

@ -15,7 +15,7 @@ export const SUPPORTED_LOCALES = {
'pt-PT': 'Português',
'zh-CN': 'Chinese',
'no-NO': 'Norsk Bokmål',
} as const
} as Record<string, string>
export type SupportedLocale = keyof typeof SUPPORTED_LOCALES
@ -23,12 +23,12 @@ export const DEFAULT_LANGUAGE: SupportedLocale= 'en'
export type ISOLanguage = string
// we load all messages async
// we load all messsages async
export const i18n = createI18n({
fallbackLocale: DEFAULT_LANGUAGE,
legacy: false,
messages: {
[DEFAULT_LANGUAGE]: langEN,
en: langEN,
} as Record<SupportedLocale, any>,
})
@ -54,16 +54,16 @@ export async function setLanguage(lang: SupportedLocale = getCurrentLanguage()):
}
export function getCurrentLanguage(): SupportedLocale {
const savedLanguage = localStorage.getItem('language') as SupportedLocale | null
const savedLanguage = localStorage.getItem('language')
if (savedLanguage !== null) {
return savedLanguage
}
const browserLanguage = navigator.language
const language = Object.keys(SUPPORTED_LOCALES).find(langKey => {
const language: SupportedLocale | undefined = Object.keys(SUPPORTED_LOCALES).find(langKey => {
return langKey === browserLanguage || langKey.startsWith(browserLanguage + '-')
}) as SupportedLocale | undefined
})
return language || DEFAULT_LANGUAGE
}

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,4 @@
// in order to use postcss-preset-env correctly we need some client side plugins
import focusWithinInit from 'postcss-focus-within/browser'
import cssHasPseudo from 'css-has-pseudo/browser'
import focusWithin from 'focus-within'
focusWithinInit()
cssHasPseudo(document)
focusWithin(document)

View File

@ -22,7 +22,7 @@
<x-button @click="setDefaultFilters">Reset</x-button>
</div>
</div>
<fancycheckbox is-block v-model="filters.showTasksWithoutDates">
<fancycheckbox class="is-block" v-model="filters.showTasksWithoutDates">
{{ $t('project.gantt.showTasksWithoutDates') }}
</fancycheckbox>
</div>

View File

@ -415,7 +415,6 @@ async function updateTaskPosition(e) {
: e.newIndex
const task = newBucket.tasks[newTaskIndex]
const oldBucket = buckets.value.find(b => b.id === task.bucketId)
const taskBefore = newBucket.tasks[newTaskIndex - 1] ?? null
const taskAfter = newBucket.tasks[newTaskIndex + 1] ?? null
taskUpdating.value[task.id] = true
@ -426,13 +425,6 @@ async function updateTaskPosition(e) {
taskBefore !== null ? taskBefore.kanbanPosition : null,
taskAfter !== null ? taskAfter.kanbanPosition : null,
)
if (
oldBucket !== undefined && // This shouldn't actually be `undefined`, but let's play it safe.
newBucket.id !== oldBucket.id &&
newBucket.isDoneBucket !== oldBucket.isDoneBucket
) {
newTask.done = newBucket.isDoneBucket
}
try {
await taskStore.update(newTask)

View File

@ -11,10 +11,10 @@
</x-button>
</template>
</datepicker-with-range>
<fancycheckbox @update:model-value="setShowNulls" class="mr-2">
<fancycheckbox @change="setShowNulls" class="mr-2">
{{ $t('task.show.noDates') }}
</fancycheckbox>
<fancycheckbox @update:model-value="setShowOverdue">
<fancycheckbox @change="setShowOverdue">
{{ $t('task.show.overdue') }}
</fancycheckbox>
</p>

View File

@ -81,7 +81,6 @@ export default defineConfig(({mode}) => {
// See also './src/polyfills.ts'
features: {
'focus-within-pseudo-class': true,
'has-pseudo-class': true,
},
}),
],