This commit is contained in:
Marek Lenczewski
2026-04-11 15:52:33 +02:00
parent afc02abd0a
commit efe0cfe361
5 changed files with 138 additions and 67 deletions

View File

@@ -1,33 +1,34 @@
package de.haushalt.app
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.CenterAlignedTopAppBar
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.foundation.layout.statusBarsPadding
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
import androidx.navigation.NavController
import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable
import androidx.navigation.compose.currentBackStackEntryAsState
import androidx.navigation.compose.rememberNavController
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun MainScreen() {
val navController = rememberNavController()
val backStackEntry by navController.currentBackStackEntryAsState()
val currentRoute = backStackEntry?.destination?.route
Scaffold(
topBar = {
Column {
Navigation()
Subnavigation()
}
}
topBar = { Breadcrumb(currentRoute = currentRoute, navController = navController) }
) { innerPadding ->
NavHost(
navController = navController,
@@ -39,20 +40,43 @@ fun MainScreen() {
}
}
@OptIn(ExperimentalMaterial3Api::class)
@Composable
private fun Navigation() {
CenterAlignedTopAppBar(
title = { Text("Haushalt") }
private fun Breadcrumb(currentRoute: String?, navController: NavController) {
// Trail: (route, label). Erweitert sich pro neuer Destination in späteren Modulen.
val trail = listOf(
"start" to "Haushalt",
)
}
@Composable
private fun Subnavigation() {
Surface(
modifier = Modifier.fillMaxWidth(),
color = MaterialTheme.colorScheme.surfaceContainer
) {
Row(
modifier = Modifier
.fillMaxWidth()
.height(32.dp),
color = MaterialTheme.colorScheme.surfaceVariant
) {}
.statusBarsPadding()
.padding(horizontal = 16.dp, vertical = 12.dp),
verticalAlignment = Alignment.CenterVertically
) {
trail.forEachIndexed { index, (route, label) ->
if (index > 0) {
Text(
text = "",
modifier = Modifier.padding(horizontal = 6.dp),
color = MaterialTheme.colorScheme.onSurfaceVariant
)
}
val isCurrent = route == currentRoute
Text(
text = label,
fontWeight = if (isCurrent) FontWeight.SemiBold else FontWeight.Normal,
modifier = if (isCurrent) {
Modifier
} else {
Modifier.clickable {
navController.popBackStack(route, inclusive = false)
}
}
)
}
}
}
}

View File

@@ -1,16 +1,33 @@
<script setup>
import { RouterView } from 'vue-router'
import { computed } from 'vue'
import { RouterView, RouterLink, useRoute } from 'vue-router'
const route = useRoute()
const crumbs = computed(() => [
{ label: 'Haushalt', to: '/' },
...(route.meta.breadcrumb ?? []),
])
</script>
<template>
<header class="navigation">
<header class="breadcrumb-bar">
<div class="container">
<span class="brand">Haushalt</span>
<nav class="breadcrumb" aria-label="Breadcrumb">
<template v-for="(crumb, i) in crumbs" :key="i">
<span v-if="i > 0" class="sep"></span>
<RouterLink
v-if="crumb.to && i < crumbs.length - 1"
:to="crumb.to"
class="crumb"
>
{{ crumb.label }}
</RouterLink>
<span v-else class="crumb current">{{ crumb.label }}</span>
</template>
</nav>
</div>
</header>
<nav class="subnavigation">
<div class="container"></div>
</nav>
<main class="main-area">
<div class="container">
<RouterView />
@@ -19,29 +36,40 @@ import { RouterView } from 'vue-router'
</template>
<style scoped>
.navigation {
background: var(--nav-bg);
.breadcrumb-bar {
background: var(--breadcrumb-bg);
border-bottom: 1px solid var(--border);
}
.navigation .container {
.breadcrumb-bar .container {
display: flex;
align-items: center;
height: 3rem;
}
.brand {
font-size: 1.125rem;
.breadcrumb {
display: flex;
align-items: center;
gap: 0.25rem;
font-size: 0.9375rem;
}
.crumb {
color: var(--text);
text-decoration: none;
}
.crumb:hover {
text-decoration: underline;
}
.crumb.current {
font-weight: 600;
}
.subnavigation {
background: var(--subnav-bg);
border-bottom: 1px solid var(--border);
}
.subnavigation .container {
height: 2rem;
.sep {
color: var(--text-muted);
padding: 0 0.25rem;
}
.main-area .container {

View File

@@ -7,6 +7,7 @@ const router = createRouter({
path: '/',
name: 'start',
component: () => import('../views/Startpage.vue'),
meta: { breadcrumb: [] },
},
],
})

View File

@@ -1,9 +1,9 @@
:root {
--bg: #ffffff;
--text: #1a1a1a;
--text-muted: #6b7280;
--border: #e5e5e5;
--nav-bg: #f7f7f8;
--subnav-bg: #f0f0f2;
--breadcrumb-bg: #f7f7f8;
color-scheme: light dark;
}

View File

@@ -17,44 +17,62 @@ Implementierungs-Schritte als Feature-Module - WIE es gebaut wird
## Backend
- nothing
## Frontend
- App.vue - layout: navigation, subnavigation, main area
- App.vue - layout: breadcrumb, main area
- router - / start page route
- Startpage.vue - start page
- Startpage.vue - start page, no content
## App
- MainScreen.kt - layout: navigation, subnavigation, main area
- StartScreen.kt - start page
- MainScreen.kt - layout: breadcrumb, main area
- StartScreen.kt - start page, no content
## Features
- Standard layout for all pages: navigation top, subnavigation below, main area
- Start page as first instance, empty content
- Standard layout for all pages: breadcrumb, main area
# Task module
## Backend
- Task - Task entity
- id, name, date, status
- id, name, date (due), status
- TaskStatus - Enum for task status
- active, done
- active, done, inactive
- TaskController - Task routes
- index, show, create, update, delete, toggle
- index, show, create, update, delete, toggle (active/done)
- TaskManager - Task CRUD
- create, update, delete, toggle
- TaskRepository - Default task queries
- TaskDto - Dto for create und update task
- currentTasks()
- TaskDto - Dto for create and update task
## Frontend
- route/index.js - /tasks tasks routing
- Task.Vue - Display all tasks
- TaskDetail.vue - Create and edit tasks
- Startpage.vue - quader button for tasks
- App.vue - register task routes in breadcrumb.
- router - tasks routes /tasks, /tasks/all, /tasks/create, /tasks/:id
- Task.vue
- Display current tasks (now to +2 weeks and without date) as list with name (done strikethrough), onclick toggle status, order by date (no-date then date asc, hide inactive)
- top right nav - list icon (all tasks), + icon (create), eye icon (toggle task visibility by active/done)
- TaskAll.vue
- Display all tasks as list with name (done strikethrough), pencil icon (edit), bin icon (delete), onclick toggle status, order by date (no-date then date asc then inactive asc)
- top right nav - + icon (create)
- TaskCreate.vue - Display form with name-text, date-date, status-select, save-button, abort-button
- TaskEdit.vue
- Display form with name-text, date-date, status-select, update-button, reset-button, abort-button, use current values
- api.js - API routes to symfony
## App
- StartScreen.kt - quader button for tasks
- MainScreen.kt - register task routes in breadcrumb.
- NavHost - tasks routes /tasks, /tasks/all, /tasks/create, /tasks/:id
- TaskScreen.kt
- Display current tasks (now to +2 weeks and without date) as list with name (done strikethrough), onclick toggle status, order by date (no-date then date asc, hide inactive)
- top right nav - list icon (all tasks), + icon (create), eye icon (toggle task visibility by active/done)
- TaskAllScreen.kt
- Display all tasks as list with name (done strikethrough), pencil icon (edit), bin icon (delete), onclick toggle status, order by date (no-date then date asc then inactive asc)
- top right nav - + icon (create)
- TaskCreateScreen.kt - Display form with name-text, date-date, status-select, save-button, abort-button
- TaskEditScreen.kt
- Display form with name-text, date-date, status-select, update-button, reset-button, abort-button, use current values
- TaskApi.kt - API calls to symfony
## Features
- Task page
- navigation - tasks
- subnavigation - all tasks, new task, categories
- Start page
- main area - quader button to task page
- Task page
- main area - list all tasks, each task has name, status in badge, edit button, onclick toggle task status
-
- Task detal page
- main area -
- Start page: task button
- Task page: current tasks ordered by date, filter done
- TaskAll page: all tasks ordered by date and status, delete task
- TaskCreate page: create task
- TaskEdit page: update task
# Category module
## Backend