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

View File

@@ -1,16 +1,33 @@
<script setup> <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> </script>
<template> <template>
<header class="navigation"> <header class="breadcrumb-bar">
<div class="container"> <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> </div>
</header> </header>
<nav class="subnavigation">
<div class="container"></div>
</nav>
<main class="main-area"> <main class="main-area">
<div class="container"> <div class="container">
<RouterView /> <RouterView />
@@ -19,29 +36,40 @@ import { RouterView } from 'vue-router'
</template> </template>
<style scoped> <style scoped>
.navigation { .breadcrumb-bar {
background: var(--nav-bg); background: var(--breadcrumb-bg);
border-bottom: 1px solid var(--border); border-bottom: 1px solid var(--border);
} }
.navigation .container { .breadcrumb-bar .container {
display: flex; display: flex;
align-items: center; align-items: center;
height: 3rem; height: 3rem;
} }
.brand { .breadcrumb {
font-size: 1.125rem; 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; font-weight: 600;
} }
.subnavigation { .sep {
background: var(--subnav-bg); color: var(--text-muted);
border-bottom: 1px solid var(--border); padding: 0 0.25rem;
}
.subnavigation .container {
height: 2rem;
} }
.main-area .container { .main-area .container {

View File

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

View File

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

View File

@@ -17,44 +17,62 @@ Implementierungs-Schritte als Feature-Module - WIE es gebaut wird
## Backend ## Backend
- nothing - nothing
## Frontend ## Frontend
- App.vue - layout: navigation, subnavigation, main area - App.vue - layout: breadcrumb, main area
- router - / start page route - router - / start page route
- Startpage.vue - start page - Startpage.vue - start page, no content
## App ## App
- MainScreen.kt - layout: navigation, subnavigation, main area - MainScreen.kt - layout: breadcrumb, main area
- StartScreen.kt - start page - StartScreen.kt - start page, no content
## Features ## Features
- Standard layout for all pages: navigation top, subnavigation below, main area - Standard layout for all pages: breadcrumb, main area
- Start page as first instance, empty content
# Task module # Task module
## Backend ## Backend
- Task - Task entity - Task - Task entity
- id, name, date, status - id, name, date (due), status
- TaskStatus - Enum for task status - TaskStatus - Enum for task status
- active, done - active, done, inactive
- TaskController - Task routes - TaskController - Task routes
- index, show, create, update, delete, toggle - index, show, create, update, delete, toggle (active/done)
- TaskManager - Task CRUD - TaskManager - Task CRUD
- create, update, delete, toggle - create, update, delete, toggle
- TaskRepository - Default task queries - TaskRepository - Default task queries
- TaskDto - Dto for create und update task - currentTasks()
- TaskDto - Dto for create and update task
## Frontend ## Frontend
- route/index.js - /tasks tasks routing - Startpage.vue - quader button for tasks
- Task.Vue - Display all tasks - App.vue - register task routes in breadcrumb.
- TaskDetail.vue - Create and edit tasks - 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 - 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 ## Features
- Task page - Start page: task button
- navigation - tasks - Task page: current tasks ordered by date, filter done
- subnavigation - all tasks, new task, categories - TaskAll page: all tasks ordered by date and status, delete task
- Start page - TaskCreate page: create task
- main area - quader button to task page - TaskEdit page: update task
- 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 -
# Category module # Category module
## Backend ## Backend