Skip to content

Commit 15dd40c

Browse files
committed
Updated plan overview and filtered to just upcoming plans
1 parent ad42a89 commit 15dd40c

File tree

4 files changed

+72
-32
lines changed

4 files changed

+72
-32
lines changed

src/helpers/SecondaryMenuHelper.ts

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -83,13 +83,11 @@ export class SecondaryMenuHelper {
8383
const canViewPlans = UserHelper.checkAccess(Permissions.membershipApi.plans.edit) || data?.isMinistryMember;
8484
if (canViewPlans) {
8585
menuItems.push({ url: "/serving", label: Locale.label("components.wrapper.plans"), icon: "assignment" });
86-
menuItems.push({ url: "/serving/overview", label: "Overview", icon: "grid_on" });
8786
menuItems.push({ url: "/serving/songs", label: Locale.label("components.wrapper.songs"), icon: "music_note" });
8887
}
8988
menuItems.push({ url: "/serving/tasks", label: Locale.label("components.wrapper.tasks"), icon: "list_alt" });
9089

91-
if (path.startsWith("/serving/overview")) label = "Overview";
92-
else if (path.startsWith("/serving/songs")) label = Locale.label("components.wrapper.songs");
90+
if (path.startsWith("/serving/songs")) label = Locale.label("components.wrapper.songs");
9391
else if (path.startsWith("/serving/tasks")) label = Locale.label("components.wrapper.tasks");
9492
else if (path.startsWith("/serving")) label = Locale.label("components.wrapper.plans");
9593

src/serving/ServingOverviewPage.tsx

Lines changed: 8 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
import React from "react";
22
import { ApiHelper, ArrayHelper, DateHelper, ExportLink, Loading, PageHeader, type PersonInterface } from "@churchapps/apphelper";
3-
import { Box, FormControl, InputLabel, MenuItem, Select, Table, TableBody, TableCell, TableContainer, TableHead, TableRow, Paper, TextField } from "@mui/material";
3+
import { Box, Table, TableBody, TableCell, TableContainer, TableHead, TableRow, Paper, TextField } from "@mui/material";
44
import { useQuery } from "@tanstack/react-query";
5-
import { type GroupInterface } from "@churchapps/helpers";
5+
import { useSearchParams } from "react-router-dom";
66

77
interface OverviewRow {
88
serviceDate: string;
@@ -37,23 +37,21 @@ const getDefaultDates = () => {
3737
};
3838

3939
export const ServingOverviewPage = () => {
40+
const [searchParams] = useSearchParams();
4041
const defaults = getDefaultDates();
4142
const [startDate, setStartDate] = React.useState(defaults.startDate);
4243
const [endDate, setEndDate] = React.useState(defaults.endDate);
43-
const [ministryId, setMinistryId] = React.useState<string>("");
44-
45-
const ministries = useQuery<GroupInterface[]>({
46-
queryKey: ["/groups/tag/ministry", "MembershipApi"],
47-
placeholderData: []
48-
});
44+
const ministryId = searchParams.get("ministryId") || "";
45+
const planTypeId = searchParams.get("planTypeId") || "";
4946

5047
const overviewData = useQuery<OverviewRow[]>({
51-
queryKey: ["/plans/overview", "DoingApi", startDate, endDate, ministryId],
48+
queryKey: ["/plans/overview", "DoingApi", startDate, endDate, ministryId, planTypeId],
5249
enabled: !!startDate && !!endDate,
5350
placeholderData: [],
5451
queryFn: async () => {
5552
let url = `/plans/overview?startDate=${startDate}&endDate=${endDate}`;
5653
if (ministryId) url += `&ministryId=${ministryId}`;
54+
if (planTypeId) url += `&planTypeId=${planTypeId}`;
5755
return ApiHelper.get(url, "DoingApi");
5856
}
5957
});
@@ -138,23 +136,14 @@ export const ServingOverviewPage = () => {
138136
<Box sx={{ display: "flex", gap: 2, mb: 3, flexWrap: "wrap", alignItems: "center" }}>
139137
<TextField label="Start Date" type="date" value={startDate} onChange={(e) => setStartDate(e.target.value)} slotProps={{ inputLabel: { shrink: true } }} size="small" />
140138
<TextField label="End Date" type="date" value={endDate} onChange={(e) => setEndDate(e.target.value)} slotProps={{ inputLabel: { shrink: true } }} size="small" />
141-
<FormControl size="small" sx={{ minWidth: 200 }}>
142-
<InputLabel>Ministry</InputLabel>
143-
<Select value={ministryId} label="Ministry" onChange={(e) => setMinistryId(e.target.value)}>
144-
<MenuItem value="">All Ministries</MenuItem>
145-
{(ministries.data || []).map(m => (
146-
<MenuItem key={m.id} value={m.id}>{m.name}</MenuItem>
147-
))}
148-
</Select>
149-
</FormControl>
150139
<ExportLink data={csvData} customHeaders={csvHeaders} filename="serving-overview.csv" text="Export CSV" />
151140
</Box>
152141

153142
{/* Grid Table */}
154143
{rows.length === 0 ? (
155144
<Box sx={{ textAlign: "center", py: 4, color: "text.secondary" }}>No serving data found for the selected date range.</Box>
156145
) : (
157-
<TableContainer component={Paper} sx={{ maxHeight: "70vh" }}>
146+
<TableContainer component={Paper} sx={{ maxHeight: "70vh", overflowX: "auto" }}>
158147
<Table stickyHeader size="small">
159148
<TableHead>
160149
<TableRow>

src/serving/components/PlanList.tsx

Lines changed: 49 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import React, { useCallback, memo } from "react";
2-
import { Box, Card, CardContent, Typography, Stack, Paper, Chip, Avatar, Button, Menu, MenuItem, ListItemIcon, ListItemText } from "@mui/material";
3-
import { Add as AddIcon, ArrowDropDown as ArrowDropDownIcon, Assignment as AssignmentIcon, CalendarMonth as CalendarIcon, Edit as EditIcon, EventNote as EventNoteIcon, MenuBook as MenuBookIcon, DateRange as DateRangeIcon } from "@mui/icons-material";
2+
import { Box, Card, CardContent, Typography, Stack, Paper, Chip, Avatar, Button, Menu, MenuItem, ListItemIcon, ListItemText, FormControlLabel, Switch } from "@mui/material";
3+
import { Add as AddIcon, ArrowDropDown as ArrowDropDownIcon, Assignment as AssignmentIcon, CalendarMonth as CalendarIcon, Edit as EditIcon, EventNote as EventNoteIcon, MenuBook as MenuBookIcon, DateRange as DateRangeIcon, History as HistoryIcon } from "@mui/icons-material";
44
import { Link } from "react-router-dom";
55
import { type GroupInterface } from "@churchapps/helpers";
66
import { type PlanInterface } from "../../helpers";
@@ -20,6 +20,7 @@ interface Props {
2020

2121
export const PlanList = memo((props: Props) => {
2222
const [plan, setPlan] = React.useState<PlanInterface>(null);
23+
const [showPast, setShowPast] = React.useState(false);
2324
const [showLessonSchedule, setShowLessonSchedule] = React.useState(false);
2425
const [showBulkSchedule, setShowBulkSchedule] = React.useState(false);
2526
const [lessonMenuAnchor, setLessonMenuAnchor] = React.useState<null | HTMLElement>(null);
@@ -31,13 +32,26 @@ export const PlanList = memo((props: Props) => {
3132
});
3233

3334
const plans = React.useMemo(() => {
35+
let result: PlanInterface[];
3436
// When planTypeId is provided, the API already returns filtered data
3537
if (props.planTypeId) {
36-
return plansQuery.data || [];
38+
result = plansQuery.data || [];
39+
} else {
40+
// When no planTypeId, filter by ministry only
41+
result = ArrayHelper.getAll(plansQuery.data || [], "ministryId", props.ministry.id);
3742
}
38-
// When no planTypeId, filter by ministry only
39-
return ArrayHelper.getAll(plansQuery.data || [], "ministryId", props.ministry.id);
40-
}, [plansQuery.data, props.ministry.id, props.planTypeId]);
43+
if (!showPast) {
44+
const today = new Date();
45+
today.setHours(0, 0, 0, 0);
46+
result = result.filter(p => {
47+
if (!p.serviceDate) return true;
48+
const d = new Date(p.serviceDate);
49+
d.setHours(0, 0, 0, 0);
50+
return d >= today;
51+
});
52+
}
53+
return result;
54+
}, [plansQuery.data, props.ministry.id, props.planTypeId, showPast]);
4155

4256
const addPlan = useCallback(() => {
4357
const lastSunday = DateHelper.getLastSunday();
@@ -103,7 +117,9 @@ export const PlanList = memo((props: Props) => {
103117
return <Loading />;
104118
}
105119

106-
if (plans.length === 0) {
120+
const hasPastPlans = !showPast && plans.length === 0 && (plansQuery.data || []).length > 0;
121+
122+
if (plans.length === 0 && !hasPastPlans) {
107123
return (
108124
<Box>
109125
<Paper
@@ -182,6 +198,11 @@ export const PlanList = memo((props: Props) => {
182198
<Typography variant="h5" sx={{ fontWeight: 600, color: "text.primary" }}>
183199
{Locale.label("plans.planList.plans")}
184200
</Typography>
201+
<FormControlLabel
202+
control={<Switch size="small" checked={showPast} onChange={(e) => setShowPast(e.target.checked)} />}
203+
label={<Stack direction="row" alignItems="center" spacing={0.5}><HistoryIcon fontSize="small" /><Typography variant="body2">Show Past</Typography></Stack>}
204+
sx={{ ml: 2 }}
205+
/>
185206
</Stack>
186207
{canEdit && (
187208
<Stack direction="row" spacing={1}>
@@ -206,6 +227,27 @@ export const PlanList = memo((props: Props) => {
206227
</Stack>
207228
</Box>
208229

230+
{hasPastPlans && (
231+
<Paper
232+
sx={{
233+
p: 4,
234+
textAlign: "center",
235+
backgroundColor: "var(--bg-sub)",
236+
border: "1px dashed",
237+
borderColor: "var(--border-main)",
238+
borderRadius: 2,
239+
mb: 3
240+
}}>
241+
<EventNoteIcon sx={{ fontSize: 48, color: "text.secondary", mb: 1 }} />
242+
<Typography variant="h6" color="text.secondary" gutterBottom>
243+
No Upcoming Plans
244+
</Typography>
245+
<Typography variant="body2" color="text.secondary">
246+
There are no plans scheduled for today or later. Use the "Show Past" toggle above to view past plans.
247+
</Typography>
248+
</Paper>
249+
)}
250+
209251
<Stack spacing={2} sx={{ mb: 4 }}>
210252
{plans.map((p) => (
211253
<Card

src/serving/planTypes/PlanTypePage.tsx

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import React from "react";
2-
import { useParams } from "react-router-dom";
3-
import { Box, Container, Typography } from "@mui/material";
2+
import { useParams, Link } from "react-router-dom";
3+
import { Box, Button, Container, Typography } from "@mui/material";
4+
import { GridOn as GridOnIcon } from "@mui/icons-material";
45
import { Loading, PageHeader, Locale } from "@churchapps/apphelper";
56
import { useQuery } from "@tanstack/react-query";
67
import { type GroupInterface } from "@churchapps/helpers";
@@ -46,7 +47,17 @@ export const PlanTypePage = () => {
4647
<PageHeader
4748
title={planType.data.name || Locale.label("plans.planTypePage.planType")}
4849
subtitle={Locale.label("plans.planTypePage.subtitle")}
49-
/>
50+
>
51+
<Button
52+
component={Link}
53+
to={`/serving/overview?planTypeId=${planType.data.id}&ministryId=${planType.data.ministryId}`}
54+
variant="outlined"
55+
startIcon={<GridOnIcon />}
56+
sx={{ color: "#FFF", borderColor: "rgba(255,255,255,0.5)", "&:hover": { borderColor: "#FFF", backgroundColor: "rgba(255,255,255,0.1)" } }}
57+
>
58+
Overview
59+
</Button>
60+
</PageHeader>
5061
<Box sx={{
5162
position: "absolute",
5263
top: 0,

0 commit comments

Comments
 (0)