update
This commit is contained in:
@@ -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",
|
||||||
)
|
)
|
||||||
}
|
|
||||||
|
|
||||||
@Composable
|
|
||||||
private fun Subnavigation() {
|
|
||||||
Surface(
|
Surface(
|
||||||
|
modifier = Modifier.fillMaxWidth(),
|
||||||
|
color = MaterialTheme.colorScheme.surfaceContainer
|
||||||
|
) {
|
||||||
|
Row(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxWidth()
|
.statusBarsPadding()
|
||||||
.height(32.dp),
|
.padding(horizontal = 16.dp, vertical = 12.dp),
|
||||||
color = MaterialTheme.colorScheme.surfaceVariant
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
@@ -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: [] },
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
64
module.md
64
module.md
@@ -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
|
||||||
|
|||||||
Reference in New Issue
Block a user