Mobile Dropdowns und Aktionen vereinheitlichen
All checks were successful
CI / Build and Deploy (push) Successful in 2m17s
All checks were successful
CI / Build and Deploy (push) Successful in 2m17s
This commit is contained in:
@@ -271,6 +271,31 @@ export function BudgetColumn({
|
|||||||
|
|
||||||
const selectedBudget = group.budgets.find((budget) => budget.id === selectedBudgetId) ?? group.budgets[0] ?? null;
|
const selectedBudget = group.budgets.find((budget) => budget.id === selectedBudgetId) ?? group.budgets[0] ?? null;
|
||||||
const visibleBudgets = isCompactLayout && selectedBudget ? [selectedBudget] : group.budgets;
|
const visibleBudgets = isCompactLayout && selectedBudget ? [selectedBudget] : group.budgets;
|
||||||
|
const mobilePrimarySelectSx = {
|
||||||
|
width: "100%",
|
||||||
|
"& .MuiOutlinedInput-root": {
|
||||||
|
minHeight: 64,
|
||||||
|
borderRadius: "28px",
|
||||||
|
backgroundColor: alpha(theme.palette.background.paper, isDark ? 0.72 : 0.96),
|
||||||
|
"& fieldset": {
|
||||||
|
borderColor: alpha(theme.palette.primary.main, isDark ? 0.54 : 0.38)
|
||||||
|
},
|
||||||
|
"&:hover fieldset": {
|
||||||
|
borderColor: alpha(theme.palette.primary.main, 0.72)
|
||||||
|
},
|
||||||
|
"&.Mui-focused fieldset": {
|
||||||
|
borderWidth: 2,
|
||||||
|
borderColor: theme.palette.primary.main
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"& .MuiInputLabel-root": {
|
||||||
|
color: theme.palette.text.secondary
|
||||||
|
},
|
||||||
|
"& .MuiInputBase-input": {
|
||||||
|
fontSize: "1.08rem",
|
||||||
|
fontWeight: 600
|
||||||
|
}
|
||||||
|
} as const;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Card
|
<Card
|
||||||
@@ -441,6 +466,7 @@ export function BudgetColumn({
|
|||||||
value={selectedBudget?.id ?? ""}
|
value={selectedBudget?.id ?? ""}
|
||||||
onChange={(event) => setSelectedBudgetId(event.target.value)}
|
onChange={(event) => setSelectedBudgetId(event.target.value)}
|
||||||
fullWidth
|
fullWidth
|
||||||
|
sx={mobilePrimarySelectSx}
|
||||||
>
|
>
|
||||||
{group.budgets.map((budget) => (
|
{group.budgets.map((budget) => (
|
||||||
<MenuItem key={budget.id} value={budget.id}>
|
<MenuItem key={budget.id} value={budget.id}>
|
||||||
|
|||||||
@@ -175,6 +175,17 @@ function sortManagedUsersList(users: DashboardManagedUser[]) {
|
|||||||
|
|
||||||
|
|
||||||
type MobileSection = "overview" | "actions";
|
type MobileSection = "overview" | "actions";
|
||||||
|
type MobileAction =
|
||||||
|
| "expense"
|
||||||
|
| "budgetRelease"
|
||||||
|
| "workingGroup"
|
||||||
|
| "budget"
|
||||||
|
| "periods"
|
||||||
|
| "backup"
|
||||||
|
| "userCreate"
|
||||||
|
| "approvalThreshold"
|
||||||
|
| "users"
|
||||||
|
| "logs";
|
||||||
type DesktopSection = "overview" | "budgetGroups" | "periods" | "users" | "logs";
|
type DesktopSection = "overview" | "budgetGroups" | "periods" | "users" | "logs";
|
||||||
const currencyFormatter = new Intl.NumberFormat("de-DE", {
|
const currencyFormatter = new Intl.NumberFormat("de-DE", {
|
||||||
style: "currency",
|
style: "currency",
|
||||||
@@ -300,6 +311,26 @@ export function DashboardShell({
|
|||||||
...(canManageAccounts ? [{ value: "users" as const, label: "Nutzerverwaltung" }] : []),
|
...(canManageAccounts ? [{ value: "users" as const, label: "Nutzerverwaltung" }] : []),
|
||||||
...(canManageAccounts ? [{ value: "logs" as const, label: "Backup & Log" }] : [])
|
...(canManageAccounts ? [{ value: "logs" as const, label: "Backup & Log" }] : [])
|
||||||
];
|
];
|
||||||
|
const mobileActions = [
|
||||||
|
{ value: "expense" as const, label: "Neue Ausgabe" },
|
||||||
|
...(canManagePeriods ? [{ value: "budgetRelease" as const, label: "Bereits an AG übergeben" }] : []),
|
||||||
|
...(canManageBudgets(viewer.role)
|
||||||
|
? [
|
||||||
|
{ value: "workingGroup" as const, label: "AG anlegen" },
|
||||||
|
{ value: "budget" as const, label: "Budget anlegen" }
|
||||||
|
]
|
||||||
|
: []),
|
||||||
|
...(canManagePeriods ? [{ value: "periods" as const, label: "Zeitraum" }] : []),
|
||||||
|
...(canManageAccounts
|
||||||
|
? [
|
||||||
|
{ value: "backup" as const, label: "CSV-Backup" },
|
||||||
|
{ value: "userCreate" as const, label: "Nutzer anlegen" },
|
||||||
|
{ value: "approvalThreshold" as const, label: "Freigabe-Schwelle" },
|
||||||
|
{ value: "users" as const, label: "Nutzer verwalten" },
|
||||||
|
{ value: "logs" as const, label: "Änderungsverlauf" }
|
||||||
|
]
|
||||||
|
: [])
|
||||||
|
];
|
||||||
const showDesktopSectionTabs = !isCompactLayout && desktopSections.length > 1;
|
const showDesktopSectionTabs = !isCompactLayout && desktopSections.length > 1;
|
||||||
|
|
||||||
const defaultEditableGroup =
|
const defaultEditableGroup =
|
||||||
@@ -340,6 +371,7 @@ export function DashboardShell({
|
|||||||
const [message, setMessage] = useState<DashboardMessage | null>(null);
|
const [message, setMessage] = useState<DashboardMessage | null>(null);
|
||||||
const [busy, setBusy] = useState(false);
|
const [busy, setBusy] = useState(false);
|
||||||
const [mobileSection, setMobileSection] = useState<MobileSection>("overview");
|
const [mobileSection, setMobileSection] = useState<MobileSection>("overview");
|
||||||
|
const [selectedMobileAction, setSelectedMobileAction] = useState<MobileAction>("expense");
|
||||||
const [desktopSection, setDesktopSection] = useState<DesktopSection>("overview");
|
const [desktopSection, setDesktopSection] = useState<DesktopSection>("overview");
|
||||||
const [selectedCurrentPeriodId, setSelectedCurrentPeriodId] = useState(currentPeriodId);
|
const [selectedCurrentPeriodId, setSelectedCurrentPeriodId] = useState(currentPeriodId);
|
||||||
const [selectedMobileGroupId, setSelectedMobileGroupId] = useState(visibleGroups[0]?.id ?? "");
|
const [selectedMobileGroupId, setSelectedMobileGroupId] = useState(visibleGroups[0]?.id ?? "");
|
||||||
@@ -377,6 +409,12 @@ export function DashboardShell({
|
|||||||
}
|
}
|
||||||
}, [desktopSection, desktopSections]);
|
}, [desktopSection, desktopSections]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!mobileActions.some((action) => action.value === selectedMobileAction)) {
|
||||||
|
setSelectedMobileAction(mobileActions[0]?.value ?? "expense");
|
||||||
|
}
|
||||||
|
}, [mobileActions, selectedMobileAction]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const directGroupId = searchParams.get("group");
|
const directGroupId = searchParams.get("group");
|
||||||
const budgetId = searchParams.get("budget");
|
const budgetId = searchParams.get("budget");
|
||||||
@@ -1450,6 +1488,32 @@ export function DashboardShell({
|
|||||||
overflow: "hidden"
|
overflow: "hidden"
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const mobilePrimarySelectSx = {
|
||||||
|
width: "100%",
|
||||||
|
"& .MuiOutlinedInput-root": {
|
||||||
|
minHeight: 64,
|
||||||
|
borderRadius: "28px",
|
||||||
|
backgroundColor: alpha(theme.palette.background.paper, isDark ? 0.72 : 0.96),
|
||||||
|
"& fieldset": {
|
||||||
|
borderColor: alpha(theme.palette.primary.main, isDark ? 0.54 : 0.38)
|
||||||
|
},
|
||||||
|
"&:hover fieldset": {
|
||||||
|
borderColor: alpha(theme.palette.primary.main, 0.72)
|
||||||
|
},
|
||||||
|
"&.Mui-focused fieldset": {
|
||||||
|
borderWidth: 2,
|
||||||
|
borderColor: theme.palette.primary.main
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"& .MuiInputLabel-root": {
|
||||||
|
color: theme.palette.text.secondary
|
||||||
|
},
|
||||||
|
"& .MuiInputBase-input": {
|
||||||
|
fontSize: "1.08rem",
|
||||||
|
fontWeight: 600
|
||||||
|
}
|
||||||
|
} as const;
|
||||||
|
|
||||||
const periodManagementPanel = canManagePeriods ? (
|
const periodManagementPanel = canManagePeriods ? (
|
||||||
<Stack spacing={2}>
|
<Stack spacing={2}>
|
||||||
<Box>
|
<Box>
|
||||||
@@ -1655,7 +1719,24 @@ export function DashboardShell({
|
|||||||
: null)
|
: null)
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{isCompactLayout || desktopSection === "overview" ? (
|
{isCompactLayout ? (
|
||||||
|
<TextField
|
||||||
|
select
|
||||||
|
label="Aktion auswählen"
|
||||||
|
value={selectedMobileAction}
|
||||||
|
onChange={(event) => setSelectedMobileAction(event.target.value as MobileAction)}
|
||||||
|
fullWidth
|
||||||
|
sx={mobilePrimarySelectSx}
|
||||||
|
>
|
||||||
|
{mobileActions.map((action) => (
|
||||||
|
<MenuItem key={action.value} value={action.value}>
|
||||||
|
{action.label}
|
||||||
|
</MenuItem>
|
||||||
|
))}
|
||||||
|
</TextField>
|
||||||
|
) : null}
|
||||||
|
|
||||||
|
{(isCompactLayout ? selectedMobileAction === "expense" : desktopSection === "overview") ? (
|
||||||
<Card sx={islandCardSx}>
|
<Card sx={islandCardSx}>
|
||||||
<CardContent sx={{ p: 3 }}>
|
<CardContent sx={{ p: 3 }}>
|
||||||
<Stack spacing={2.5}>
|
<Stack spacing={2.5}>
|
||||||
@@ -1777,7 +1858,7 @@ export function DashboardShell({
|
|||||||
</Card>
|
</Card>
|
||||||
) : null}
|
) : null}
|
||||||
|
|
||||||
{canManagePeriods && (isCompactLayout || desktopSection === "overview") ? (
|
{canManagePeriods && (isCompactLayout ? selectedMobileAction === "budgetRelease" : desktopSection === "overview") ? (
|
||||||
<Card sx={islandCardSx}>
|
<Card sx={islandCardSx}>
|
||||||
<CardContent sx={{ p: 3 }}>
|
<CardContent sx={{ p: 3 }}>
|
||||||
<Stack spacing={2.5}>
|
<Stack spacing={2.5}>
|
||||||
@@ -1869,7 +1950,7 @@ export function DashboardShell({
|
|||||||
</Card>
|
</Card>
|
||||||
) : null}
|
) : null}
|
||||||
|
|
||||||
{canManageBudgets(viewer.role) && (isCompactLayout || desktopSection === "budgetGroups") ? (
|
{canManageBudgets(viewer.role) && (isCompactLayout ? selectedMobileAction === "workingGroup" : desktopSection === "budgetGroups") ? (
|
||||||
<Card sx={islandCardSx}>
|
<Card sx={islandCardSx}>
|
||||||
<CardContent sx={{ p: 3 }}>
|
<CardContent sx={{ p: 3 }}>
|
||||||
<Stack spacing={2.5}>
|
<Stack spacing={2.5}>
|
||||||
@@ -1902,7 +1983,7 @@ export function DashboardShell({
|
|||||||
</Card>
|
</Card>
|
||||||
) : null}
|
) : null}
|
||||||
|
|
||||||
{canManageBudgets(viewer.role) && (isCompactLayout || desktopSection === "budgetGroups") ? (
|
{canManageBudgets(viewer.role) && (isCompactLayout ? selectedMobileAction === "budget" : desktopSection === "budgetGroups") ? (
|
||||||
<Card sx={islandCardSx}>
|
<Card sx={islandCardSx}>
|
||||||
<CardContent sx={{ p: 3 }}>
|
<CardContent sx={{ p: 3 }}>
|
||||||
<Stack spacing={2.5}>
|
<Stack spacing={2.5}>
|
||||||
@@ -1983,13 +2064,13 @@ export function DashboardShell({
|
|||||||
</Card>
|
</Card>
|
||||||
) : null}
|
) : null}
|
||||||
|
|
||||||
{canManagePeriods && isCompactLayout ? (
|
{canManagePeriods && isCompactLayout && selectedMobileAction === "periods" ? (
|
||||||
<Card sx={islandCardSx}>
|
<Card sx={islandCardSx}>
|
||||||
<CardContent sx={{ p: 3 }}>{periodManagementPanel}</CardContent>
|
<CardContent sx={{ p: 3 }}>{periodManagementPanel}</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
) : null}
|
) : null}
|
||||||
|
|
||||||
{canManageAccounts && (isCompactLayout || desktopSection === "logs") ? (
|
{canManageAccounts && (isCompactLayout ? selectedMobileAction === "backup" : desktopSection === "logs") ? (
|
||||||
<Card sx={islandCardSx}>
|
<Card sx={islandCardSx}>
|
||||||
<CardContent sx={{ p: 3 }}>
|
<CardContent sx={{ p: 3 }}>
|
||||||
<Stack spacing={2}>
|
<Stack spacing={2}>
|
||||||
@@ -2038,8 +2119,12 @@ export function DashboardShell({
|
|||||||
</Card>
|
</Card>
|
||||||
) : null}
|
) : null}
|
||||||
|
|
||||||
{canManageAccounts && (isCompactLayout || desktopSection === "users") ? (
|
{canManageAccounts &&
|
||||||
|
(isCompactLayout
|
||||||
|
? selectedMobileAction === "userCreate" || selectedMobileAction === "approvalThreshold"
|
||||||
|
: desktopSection === "users") ? (
|
||||||
<Stack spacing={3}>
|
<Stack spacing={3}>
|
||||||
|
{!isCompactLayout || selectedMobileAction === "userCreate" ? (
|
||||||
<Card sx={islandCardSx}>
|
<Card sx={islandCardSx}>
|
||||||
<CardContent sx={{ p: 3 }}>
|
<CardContent sx={{ p: 3 }}>
|
||||||
<Stack spacing={2.5}>
|
<Stack spacing={2.5}>
|
||||||
@@ -2141,7 +2226,9 @@ export function DashboardShell({
|
|||||||
</Stack>
|
</Stack>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
|
) : null}
|
||||||
|
|
||||||
|
{!isCompactLayout || selectedMobileAction === "approvalThreshold" ? (
|
||||||
<Card sx={islandCardSx}>
|
<Card sx={islandCardSx}>
|
||||||
<CardContent sx={{ p: 3 }}>
|
<CardContent sx={{ p: 3 }}>
|
||||||
<Stack spacing={2}>
|
<Stack spacing={2}>
|
||||||
@@ -2168,9 +2255,10 @@ export function DashboardShell({
|
|||||||
</Stack>
|
</Stack>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
|
) : null}
|
||||||
</Stack>
|
</Stack>
|
||||||
) : null}
|
) : null}
|
||||||
{canManageAccounts && (isCompactLayout || desktopSection === "users") ? (
|
{canManageAccounts && (isCompactLayout ? selectedMobileAction === "users" : desktopSection === "users") ? (
|
||||||
<Card sx={islandCardSx}>
|
<Card sx={islandCardSx}>
|
||||||
<CardContent sx={{ p: 3 }}>
|
<CardContent sx={{ p: 3 }}>
|
||||||
<Stack spacing={2}>
|
<Stack spacing={2}>
|
||||||
@@ -2412,7 +2500,7 @@ export function DashboardShell({
|
|||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
) : null}
|
) : null}
|
||||||
{canManageAccounts && (isCompactLayout || desktopSection === "logs") ? (
|
{canManageAccounts && (isCompactLayout ? selectedMobileAction === "logs" : desktopSection === "logs") ? (
|
||||||
<Card sx={islandCardSx}>
|
<Card sx={islandCardSx}>
|
||||||
<CardContent sx={{ p: 3 }}>
|
<CardContent sx={{ p: 3 }}>
|
||||||
<Stack spacing={2}>
|
<Stack spacing={2}>
|
||||||
@@ -2532,6 +2620,7 @@ export function DashboardShell({
|
|||||||
setFocusedBudgetId(null);
|
setFocusedBudgetId(null);
|
||||||
}}
|
}}
|
||||||
fullWidth
|
fullWidth
|
||||||
|
sx={mobilePrimarySelectSx}
|
||||||
>
|
>
|
||||||
{visibleGroups.map((group) => (
|
{visibleGroups.map((group) => (
|
||||||
<MenuItem key={group.id} value={group.id}>
|
<MenuItem key={group.id} value={group.id}>
|
||||||
|
|||||||
Reference in New Issue
Block a user