Skip to content

Commit 00a87dc

Browse files
Merge branch 'main' into feat/rrule-upstream-pr
2 parents 92f807c + 6ab919e commit 00a87dc

File tree

16 files changed

+433
-24
lines changed

16 files changed

+433
-24
lines changed

.github/workflows/build.yml

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
name: Build
2+
3+
on:
4+
push:
5+
branches:
6+
- main
7+
workflow_dispatch:
8+
9+
env:
10+
REGISTRY: ghcr.io
11+
IMAGE_NAME: ${{ github.repository }}
12+
13+
jobs:
14+
build:
15+
runs-on: ubuntu-latest
16+
permissions:
17+
contents: read
18+
packages: write
19+
20+
outputs:
21+
version: ${{ steps.ghd.outputs.describe }}
22+
image_tag: ${{ steps.meta.outputs.tags }}
23+
24+
steps:
25+
- name: Checkout
26+
uses: actions/checkout@v4
27+
with:
28+
fetch-depth: 0
29+
30+
- name: Git describe
31+
id: ghd
32+
uses: proudust/gh-describe@v2
33+
34+
- name: Set up Docker Buildx
35+
uses: docker/setup-buildx-action@v3
36+
37+
- name: Login to GitHub Container Registry
38+
uses: docker/login-action@v3
39+
with:
40+
registry: ${{ env.REGISTRY }}
41+
username: ${{ github.actor }}
42+
password: ${{ secrets.GITHUB_TOKEN }}
43+
44+
- name: Extract metadata
45+
id: meta
46+
uses: docker/metadata-action@v5
47+
with:
48+
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
49+
tags: |
50+
type=raw,value=latest
51+
type=sha,prefix=
52+
type=raw,value=${{ steps.ghd.outputs.describe }}
53+
54+
- name: Build and push
55+
uses: docker/build-push-action@v6
56+
with:
57+
context: .
58+
platforms: linux/amd64
59+
push: true
60+
tags: ${{ steps.meta.outputs.tags }}
61+
labels: ${{ steps.meta.outputs.labels }}
62+
build-args: |
63+
RELEASE_VERSION=${{ steps.ghd.outputs.describe }}
64+
cache-from: type=gha
65+
cache-to: type=gha,mode=max
66+
67+
- name: Output image info
68+
run: |
69+
echo "Built version: ${{ steps.ghd.outputs.describe }}"
70+
echo "Image tags: ${{ steps.meta.outputs.tags }}"
71+

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,3 +47,4 @@ devenv.local.nix
4747
PLAN.md
4848
/.crush/
4949
/.playwright-mcp
50+
.claude/

Dockerfile

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,35 +1,35 @@
1-
# syntax=docker/dockerfile:1@sha256:b6afd42430b15f2d2a4c5a02b919e98a525b785b1aaff16747d2f623364e39b6
2-
FROM --platform=$BUILDPLATFORM node:24.12.0-alpine@sha256:c921b97d4b74f51744057454b306b418cf693865e73b8100559189605f6955b8 AS frontendbuilder
1+
# syntax=docker/dockerfile:1
2+
FROM node:22-alpine AS frontendbuilder
33

44
WORKDIR /build
55

66
ENV PNPM_CACHE_FOLDER=.cache/pnpm/
77
ENV PUPPETEER_SKIP_DOWNLOAD=true
88
ENV CYPRESS_INSTALL_BINARY=0
99

10-
COPY frontend/pnpm-lock.yaml frontend/package.json frontend/.npmrc ./
10+
COPY frontend/pnpm-lock.yaml frontend/package.json frontend/.npmrc ./
1111
COPY frontend/patches ./patches
1212
RUN npm install -g corepack && corepack enable && \
1313
pnpm install --frozen-lockfile
1414
COPY frontend/ ./
1515
ARG RELEASE_VERSION=dev
1616
RUN echo "{\"VERSION\": \"${RELEASE_VERSION/-g/-}\"}" > src/version.json && pnpm run build
1717

18-
FROM --platform=$BUILDPLATFORM ghcr.io/techknowlogick/xgo:go-1.25.x@sha256:3c0082275ecc275b34a03a269ecc768e0f4d0f332e0d1584732a4a53242340fe AS apibuilder
18+
FROM golang:1.24-alpine AS apibuilder
1919

20-
RUN go install github.com/magefile/mage@latest && \
21-
mv /go/bin/mage /usr/local/go/bin
20+
RUN apk add --no-cache git make
2221

23-
WORKDIR /go/src/code.vikunja.io/api
22+
RUN go install github.com/magefile/mage@latest
23+
24+
WORKDIR /build
2425
COPY . ./
2526
COPY --from=frontendbuilder /build/dist ./frontend/dist
2627

27-
ARG TARGETOS TARGETARCH TARGETVARIANT RELEASE_VERSION
28+
ARG RELEASE_VERSION=dev
2829
ENV RELEASE_VERSION=$RELEASE_VERSION
30+
ENV CGO_ENABLED=0
2931

30-
RUN export PATH=$PATH:$GOPATH/bin && \
31-
mage build:clean && \
32-
mage release:xgo "${TARGETOS}/${TARGETARCH}/${TARGETVARIANT}"
32+
RUN mage build
3333

3434
# ┬─┐┬ ┐┌┐┐┌┐┐┬─┐┬─┐
3535
# │┬┘│ │││││││├─ │┬┘
@@ -53,5 +53,5 @@ USER 1000
5353
ENV VIKUNJA_SERVICE_ROOTPATH=/app/vikunja/
5454
ENV VIKUNJA_DATABASE_PATH=/db/vikunja.db
5555

56-
COPY --from=apibuilder /build/vikunja-* vikunja
56+
COPY --from=apibuilder /build/vikunja vikunja
5757
COPY --from=apibuilder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/

frontend/src/components/home/Navigation.vue

Lines changed: 112 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -83,29 +83,71 @@
8383
v-if="favoriteProjects.length"
8484
class="menu"
8585
>
86+
<BaseButton
87+
v-if="enhancedMode"
88+
class="menu-section-header"
89+
@click="sectionOpenState.favorites = !sectionOpenState.favorites"
90+
>
91+
<Icon
92+
icon="chevron-down"
93+
:class="{ 'section-is-collapsed': !sectionOpenState.favorites }"
94+
/>
95+
{{ $t('navigation.favorites') }}
96+
</BaseButton>
8697
<ProjectsNavigation
87-
:model-value="favoriteProjects"
98+
v-if="!enhancedMode || sectionOpenState.favorites"
99+
:model-value="favoriteProjects"
88100
:can-edit-order="false"
89101
:can-collapse="false"
102+
:hide-star-icons="enhancedMode"
103+
:show-task-counts="showTaskCounts"
90104
/>
91105
</nav>
92-
106+
93107
<nav
94108
v-if="savedFilterProjects.length"
95109
class="menu"
96110
>
111+
<BaseButton
112+
v-if="enhancedMode"
113+
class="menu-section-header"
114+
@click="sectionOpenState.filters = !sectionOpenState.filters"
115+
>
116+
<Icon
117+
icon="chevron-down"
118+
:class="{ 'section-is-collapsed': !sectionOpenState.filters }"
119+
/>
120+
{{ $t('navigation.filters') }}
121+
</BaseButton>
97122
<ProjectsNavigation
123+
v-if="!enhancedMode || sectionOpenState.filters"
98124
:model-value="savedFilterProjects"
99125
:can-edit-order="false"
100126
:can-collapse="false"
127+
:hide-star-icons="enhancedMode"
128+
:show-task-counts="showTaskCounts"
101129
/>
102130
</nav>
103131

104132
<nav class="menu">
133+
<BaseButton
134+
v-if="enhancedMode"
135+
class="menu-section-header"
136+
@click="sectionOpenState.projects = !sectionOpenState.projects"
137+
>
138+
<Icon
139+
icon="chevron-down"
140+
:class="{ 'section-is-collapsed': !sectionOpenState.projects }"
141+
/>
142+
{{ $t('navigation.myProjects') }}
143+
</BaseButton>
105144
<ProjectsNavigation
145+
v-if="!enhancedMode || sectionOpenState.projects"
106146
:model-value="projects"
107147
:can-edit-order="true"
108148
:can-collapse="true"
149+
:hide-star-icons="enhancedMode"
150+
:show-task-counts="showTaskCounts"
109151
/>
110152
</nav>
111153
</template>
@@ -125,27 +167,62 @@
125167
</template>
126168

127169
<script setup lang="ts">
128-
import {computed} from 'vue'
170+
import {computed, watch} from 'vue'
171+
import {useStorage} from '@vueuse/core'
129172
130173
import PoweredByLink from '@/components/home/PoweredByLink.vue'
131174
import Logo from '@/components/home/Logo.vue'
132175
import Loading from '@/components/misc/Loading.vue'
176+
import BaseButton from '@/components/base/BaseButton.vue'
133177
134178
import {useBaseStore} from '@/stores/base'
135179
import {useProjectStore} from '@/stores/projects'
180+
import {useAuthStore} from '@/stores/auth'
136181
import ProjectsNavigation from '@/components/home/ProjectsNavigation.vue'
137182
import type {IProject} from '@/modelTypes/IProject'
138183
import {useSidebarResize} from '@/composables/useSidebarResize'
184+
import {useProjectTaskCounts} from '@/composables/useProjectTaskCounts'
139185
140186
const baseStore = useBaseStore()
141187
const projectStore = useProjectStore()
188+
const authStore = useAuthStore()
142189
143190
const {sidebarWidth, isResizing, startResize, isMobile} = useSidebarResize()
191+
const {fetchTaskCountsForProjects} = useProjectTaskCounts()
192+
193+
// Section collapse state (persisted)
194+
type SectionState = { favorites: boolean; filters: boolean; projects: boolean }
195+
const sectionOpenState = useStorage<SectionState>('navigation-sections-open', {
196+
favorites: true,
197+
filters: true,
198+
projects: true,
199+
})
200+
201+
// Enhanced sidebar mode setting
202+
const enhancedMode = computed(() => authStore.settings?.frontendSettings?.sidebarEnhancedMode ?? false)
203+
const showTaskCounts = computed(() => authStore.settings?.frontendSettings?.sidebarShowTaskCounts ?? false)
144204
145205
// Cast readonly arrays to mutable type - the arrays are not actually mutated by the component
146206
const projects = computed(() => projectStore.notArchivedRootProjects as IProject[])
147207
const favoriteProjects = computed(() => projectStore.favoriteProjects as IProject[])
148208
const savedFilterProjects = computed(() => projectStore.savedFilterProjects as IProject[])
209+
210+
// All non-archived projects (including children) for task count fetching
211+
const allNonArchivedProjects = computed(() =>
212+
(projectStore.projectsArray as IProject[]).filter(p => !p.isArchived),
213+
)
214+
215+
// Fetch task counts when the setting is enabled and projects are loaded
216+
watch(
217+
[showTaskCounts, () => projectStore.isLoading],
218+
([showCounts, isLoading]) => {
219+
if (showCounts && !isLoading) {
220+
// Fetch counts for ALL non-archived projects (including nested children)
221+
fetchTaskCountsForProjects(allNonArchivedProjects.value)
222+
}
223+
},
224+
{immediate: true},
225+
)
149226
</script>
150227

151228
<style lang="scss" scoped>
@@ -235,4 +312,36 @@ const savedFilterProjects = computed(() => projectStore.savedFilterProjects as I
235312
.menu + .menu {
236313
padding-block-start: math.div($navbar-padding, 2);
237314
}
315+
316+
.menu-section-header {
317+
display: flex;
318+
align-items: center;
319+
gap: 0.5rem;
320+
inline-size: 100%;
321+
font-size: 0.75rem;
322+
font-weight: 600;
323+
text-transform: uppercase;
324+
letter-spacing: 0.05em;
325+
color: var(--grey-500);
326+
padding-inline-start: 1.5rem;
327+
padding-block: 0.5rem 0.25rem;
328+
margin: 0;
329+
cursor: pointer;
330+
background: transparent;
331+
border: none;
332+
text-align: start;
333+
334+
&:hover {
335+
color: var(--grey-400);
336+
}
337+
338+
.icon {
339+
font-size: 0.6rem;
340+
transition: transform $transition;
341+
}
342+
343+
.section-is-collapsed {
344+
transform: rotate(-90deg);
345+
}
346+
}
238347
</style>

frontend/src/components/home/ProjectsNavigation.vue

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@
2727
:is-loading="projectUpdating[project.id]"
2828
:can-collapse="canCollapse"
2929
:can-edit-order="canEditOrder"
30+
:hide-star-icons="hideStarIcons"
31+
:show-task-counts="showTaskCounts"
3032
:data-project-id="project.id"
3133
/>
3234
</template>
@@ -49,6 +51,8 @@ const props = defineProps<{
4951
modelValue?: IProject[],
5052
canEditOrder: boolean,
5153
canCollapse?: boolean,
54+
hideStarIcons?: boolean,
55+
showTaskCounts?: boolean,
5256
}>()
5357
const emit = defineEmits<{
5458
(e: 'update:modelValue', projects: IProject[]): void

0 commit comments

Comments
 (0)