Skip to content

Commit 490a3d4

Browse files
committed
feat(messaging): add back button to view all group chats; feat(org): hide volunteer table on mobile; fix(opportunities): prevent overflow on cards; fix(org profile): improve mobile layout; feat(org dashboard): clickable announcement popovers and clearer copy; fix(org ratings): show orange circle when no ratings
1 parent 15c7067 commit 490a3d4

8 files changed

Lines changed: 166 additions & 28 deletions

File tree

app/org/dashboard/page.tsx

Lines changed: 61 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -6,15 +6,41 @@ import { IconBell } from "@tabler/icons-react"
66
import { Pie, PieChart } from "recharts"
77
import { ChartContainer, type ChartConfig, ChartTooltip, ChartTooltipContent } from "@/components/ui/chart"
88
import { ChartBarMultiple, type PlatformBarDatum } from "../components/chart-bar-multiple"
9+
import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover"
910
import { ChartBarSingle } from "../components/chart-bar-single"
1011

1112
export default function Page() {
1213
const announcements = [
13-
{ id: "a1", title: "Welcome to CampusReach Org Portal", body: "Track sign-ups, manage events, and message students from one place.", date: "2025-09-15" },
14-
{ id: "a2", title: "New: Opportunities Page", body: "Create and manage opportunities with images, skills, and progress tracking.", date: "2025-09-20" },
15-
{ id: "a3", title: "Reminder: Verify your profile", body: "Add a logo and contact info so students recognize your org.", date: "2025-09-25" },
16-
{ id: "a4", title: "Tip: Use Messaging", body: "Reach out to interested students directly from the Messaging tab.", date: "2025-09-26" },
17-
{ id: "a5", title: "Export Reports", body: "Download monthly summaries of sign-ups and hours from the dashboard.", date: "2025-09-27" },
14+
{
15+
id: "a1",
16+
title: "New: Opportunities builder",
17+
body: "Create rich opportunities with images, required skills, and application questions. Drafts are auto‑saved.",
18+
date: "2025-09-28",
19+
},
20+
{
21+
id: "a2",
22+
title: "Volunteer messaging launched",
23+
body: "Start 1:1 chats with interested volunteers from Opportunities and the Volunteers tab.",
24+
date: "2025-10-01",
25+
},
26+
{
27+
id: "a3",
28+
title: "Ratings dashboard",
29+
body: "View event ratings distribution and comments to improve your next events.",
30+
date: "2025-10-02",
31+
},
32+
{
33+
id: "a4",
34+
title: "CSV export",
35+
body: "Download monthly reports of sign‑ups and logged hours from the dashboard.",
36+
date: "2025-10-03",
37+
},
38+
{
39+
id: "a5",
40+
title: "Maintenance window",
41+
body: "CampusReach will undergo scheduled maintenance on Oct 12, 1:00–1:30 AM ET. No downtime expected.",
42+
date: "2025-10-05",
43+
},
1844
]
1945

2046
// Active organization id
@@ -80,6 +106,9 @@ export default function Page() {
80106
{ key: "four", label: "4★", value: ratingCounts[3], fill: "var(--chart-4)" },
81107
{ key: "five", label: "5★", value: ratingCounts[4], fill: "var(--chart-5)" },
82108
]
109+
const pieData = totalRatings === 0
110+
? [{ key: "none", label: "No ratings", value: 1, fill: "hsl(30 85% 48%)" }]
111+
: ratingData
83112
const eventsFooter = React.useMemo(() => {
84113
const now = new Date()
85114
const start = new Date(now)
@@ -141,9 +170,11 @@ export default function Page() {
141170
<div className="flex items-center justify-between gap-4">
142171
<ChartContainer config={ratingChartConfig} className="h-[120px] w-[120px]">
143172
<PieChart>
144-
<ChartTooltip cursor={false} content={<ChartTooltipContent hideLabel />} />
173+
{totalRatings > 0 && (
174+
<ChartTooltip cursor={false} content={<ChartTooltipContent hideLabel />} />
175+
)}
145176
<Pie
146-
data={ratingData}
177+
data={pieData}
147178
dataKey="value"
148179
nameKey="label"
149180
stroke="0"
@@ -228,16 +259,29 @@ export default function Page() {
228259
</div>
229260
<ul className="divide-y">
230261
{announcements.map((a) => (
231-
<li key={a.id} className="flex items-start justify-between gap-4 p-3 hover:bg-muted/40">
232-
<div className="min-w-0">
233-
<div className="truncate font-medium">{a.title}</div>
234-
<p className="line-clamp-2 text-sm text-muted-foreground">{a.body}</p>
235-
</div>
236-
<div className="shrink-0">
237-
<span className="rounded bg-muted px-2 py-0.5 text-xs text-muted-foreground">
238-
{new Date(a.date).toLocaleDateString()}
239-
</span>
240-
</div>
262+
<li key={a.id} className="p-0">
263+
<Popover>
264+
<PopoverTrigger asChild>
265+
<button className="w-full flex items-start justify-between gap-4 p-3 hover:bg-muted/40 text-left">
266+
<div className="min-w-0">
267+
<div className="truncate font-medium">{a.title}</div>
268+
<p className="line-clamp-2 text-sm text-muted-foreground">{a.body}</p>
269+
</div>
270+
<div className="shrink-0">
271+
<span className="rounded bg-muted px-2 py-0.5 text-xs text-muted-foreground">
272+
{new Date(a.date).toLocaleDateString()}
273+
</span>
274+
</div>
275+
</button>
276+
</PopoverTrigger>
277+
<PopoverContent className="w-80" align="end">
278+
<div className="space-y-1">
279+
<div className="text-sm font-medium">{a.title}</div>
280+
<div className="text-[11px] text-muted-foreground">{new Date(a.date).toLocaleString()}</div>
281+
<p className="text-sm text-foreground/80 whitespace-pre-wrap">{a.body}</p>
282+
</div>
283+
</PopoverContent>
284+
</Popover>
241285
</li>
242286
))}
243287
</ul>

app/org/events/[id]/chat/page.tsx

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import * as React from "react"
44
import { useParams } from "next/navigation"
55
import { Button } from "@/components/ui/button"
6+
import Link from "next/link"
67
import { Input } from "@/components/ui/input"
78
import { Card, CardContent } from "@/components/ui/card"
89
import Image from "next/image"
@@ -89,6 +90,11 @@ export default function OrgEventChatPage() {
8990

9091
return (
9192
<main className="flex h-[calc(100dvh-60px)] flex-col p-4 md:p-6">
93+
<div className="mb-3 flex items-center justify-between">
94+
<Button asChild variant="outline" size="sm">
95+
<Link href="/org/messaging">View all group chats</Link>
96+
</Button>
97+
</div>
9298
<Card className="flex min-h-0 flex-1 flex-col">
9399
<CardContent className="flex min-h-0 flex-1 flex-col p-0">
94100
<div className="min-h-0 flex-1 overflow-auto p-4 md:p-6 space-y-3">

app/org/opportunities/page.tsx

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -426,7 +426,7 @@ export default function Page() {
426426
{upcoming.map((ev) => {
427427
const { location } = splitDesc(ev.shortDescription)
428428
return (
429-
<li key={ev.id} className="grid grid-cols-1 gap-1 md:grid-cols-12 md:items-center md:gap-x-3 p-2.5 rounded-xl border bg-card hover:shadow-sm transition h-20">
429+
<li key={ev.id} className="grid grid-cols-1 gap-y-2 md:grid-cols-12 md:items-center md:gap-x-3 p-2.5 rounded-xl border bg-card hover:shadow-sm transition overflow-hidden">
430430
<div className="md:col-span-6 space-y-1.5 min-w-0">
431431
<div className="truncate text-[14px] font-semibold leading-4">{ev.title}</div>
432432
{location && (
@@ -441,7 +441,7 @@ export default function Page() {
441441
<IconCalendar className="size-3" />
442442
<span>{formatFriendly(ev.startsAt).full}</span>
443443
</div>
444-
<div className="flex items-center gap-3 justify-center w-full max-w-[240px]">
444+
<div className="flex items-center gap-3 justify-center w-full max-w-full sm:max-w-[260px]">
445445
{(() => {
446446
const pct = Math.min(100, Math.round(((ev.signedUpCount ?? 0) / Math.max(1, ev.volunteersNeeded)) * 100))
447447
return (
@@ -453,7 +453,7 @@ export default function Page() {
453453
<span className="shrink-0 text-xs tabular-nums text-muted-foreground">{ev.signedUpCount ?? 0}/{ev.volunteersNeeded}</span>
454454
</div>
455455
</div>
456-
<div className="md:col-span-2 flex justify-end gap-1.5">
456+
<div className="md:col-span-2 flex justify-end gap-1.5 self-end md:self-auto">
457457
<Button
458458
variant="outline"
459459
size="icon"
@@ -524,7 +524,7 @@ export default function Page() {
524524
{past.map((ev) => {
525525
const { location } = splitDesc(ev.shortDescription)
526526
return (
527-
<li key={ev.id} className="grid grid-cols-1 gap-1 md:grid-cols-12 md:items-center md:gap-x-3 p-2.5 rounded-xl border bg-card hover:shadow-sm transition h-20">
527+
<li key={ev.id} className="grid grid-cols-1 gap-y-2 md:grid-cols-12 md:items-center md:gap-x-3 p-2.5 rounded-xl border bg-card hover:shadow-sm transition overflow-hidden">
528528
<div className="md:col-span-6 space-y-1.5 min-w-0">
529529
<div className="truncate text-[14px] font-semibold leading-4">{ev.title}</div>
530530
{location && (
@@ -539,7 +539,7 @@ export default function Page() {
539539
<IconCalendar className="size-3" />
540540
<span>{formatFriendly(ev.startsAt).full}</span>
541541
</div>
542-
<div className="flex items-center gap-3 justify-center w-full max-w-[240px]">
542+
<div className="flex items-center gap-3 justify-center w-full max-w-full sm:max-w-[260px]">
543543
{(() => {
544544
const pct = Math.min(100, Math.round(((ev.signedUpCount ?? 0) / Math.max(1, ev.volunteersNeeded)) * 100))
545545
return (
@@ -551,7 +551,7 @@ export default function Page() {
551551
<span className="shrink-0 text-xs tabular-nums text-muted-foreground">{ev.signedUpCount ?? 0}/{ev.volunteersNeeded}</span>
552552
</div>
553553
</div>
554-
<div className="md:col-span-2 flex justify-end gap-1.5">
554+
<div className="md:col-span-2 flex justify-end gap-1.5 self-end md:self-auto">
555555
<Button
556556
variant="outline"
557557
size="icon"

app/org/profile/page.tsx

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -231,12 +231,12 @@ export default function Page() {
231231
return (
232232
<main className="p-4 space-y-4">
233233
{/* Page header */}
234-
<div className="flex items-start justify-between gap-3">
234+
<div className="flex flex-col sm:flex-row sm:items-start sm:justify-between gap-3">
235235
<div>
236236
<h1 className="text-xl font-semibold tracking-tight">Organization profile</h1>
237237
<p className="text-sm text-muted-foreground">Add details and links to help students recognize your org.</p>
238238
</div>
239-
<div className="flex items-center gap-2">
239+
<div className="flex flex-wrap items-center gap-2 sm:ml-auto">
240240
{loading ? (
241241
<div className="text-xs text-muted-foreground">Loading…</div>
242242
) : (
@@ -276,7 +276,7 @@ export default function Page() {
276276
/>
277277
</div>
278278
{/* Logo */}
279-
<div className="flex items-center gap-4">
279+
<div className="flex flex-col sm:flex-row sm:items-center gap-4">
280280
<div className="flex items-center gap-3">
281281
<Avatar className="h-16 w-16 rounded-full overflow-hidden bg-muted">
282282
{logoPreview ? (
@@ -297,7 +297,7 @@ export default function Page() {
297297
<p className="text-xs text-muted-foreground">PNG, JPG. 512x512 recommended.</p>
298298
</div>
299299
</div>
300-
<div className="ml-auto flex items-center gap-2">
300+
<div className="sm:ml-auto flex items-center gap-2 w-full sm:w-auto">
301301
<Input
302302
type="file"
303303
accept="image/*"
@@ -326,7 +326,7 @@ export default function Page() {
326326
})()
327327
}
328328
}}
329-
className={`max-w-xs`}
329+
className={`w-full sm:max-w-xs`}
330330
/>
331331
{uploadingAvatar && <span className="text-xs text-muted-foreground">Uploading…</span>}
332332
</div>

app/org/volunteers/page.tsx

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,10 @@ import { DataTable, schema } from "../components/data-table"
55
import { Skeleton } from "@/components/ui/skeleton"
66
import { Button } from "@/components/ui/button"
77
import Link from "next/link"
8+
import { useIsMobile } from "@/hooks/use-mobile"
89

910
export default function Page() {
11+
const isMobile = useIsMobile()
1012
const [eventId, setEventId] = React.useState<string | null>(null)
1113
const [eventOptions, setEventOptions] = React.useState<{ id: string; label: string }[]>([])
1214
const [rows, setRows] = React.useState<Array<import("zod").z.infer<typeof schema>>>([])
@@ -84,6 +86,22 @@ export default function Page() {
8486
</main>
8587
)
8688
}
89+
// On phones, hide the data table and suggest using a computer
90+
if (isMobile) {
91+
return (
92+
<main className="p-6">
93+
<div className="rounded-lg border p-8 text-center">
94+
<h1 className="mb-2 text-base font-semibold">Volunteer management</h1>
95+
<p className="mx-auto mb-4 max-w-md text-sm text-muted-foreground">
96+
Managing volunteers works best on a larger screen. Please visit this page from a laptop or desktop computer.
97+
</p>
98+
<Button asChild>
99+
<Link href="/org/dashboard">Back to dashboard</Link>
100+
</Button>
101+
</div>
102+
</main>
103+
)
104+
}
87105
if (eventOptions.length === 0) {
88106
return (
89107
<main className="p-6">

components/ui/popover.tsx

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
"use client"
2+
3+
import * as React from "react"
4+
import * as PopoverPrimitive from "@radix-ui/react-popover"
5+
import { cn } from "@/lib/utils"
6+
7+
const Popover = PopoverPrimitive.Root
8+
9+
const PopoverTrigger = PopoverPrimitive.Trigger
10+
11+
const PopoverContent = React.forwardRef<
12+
React.ElementRef<typeof PopoverPrimitive.Content>,
13+
React.ComponentPropsWithoutRef<typeof PopoverPrimitive.Content>
14+
>(({ className, align = "center", sideOffset = 8, ...props }, ref) => (
15+
<PopoverPrimitive.Portal>
16+
<PopoverPrimitive.Content
17+
ref={ref}
18+
align={align}
19+
sideOffset={sideOffset}
20+
className={cn(
21+
"z-50 w-72 rounded-md border bg-popover p-3 text-popover-foreground shadow-md outline-none data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
22+
className
23+
)}
24+
{...props}
25+
/>
26+
</PopoverPrimitive.Portal>
27+
))
28+
PopoverContent.displayName = PopoverPrimitive.Content.displayName
29+
30+
export { Popover, PopoverTrigger, PopoverContent }

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
"@radix-ui/react-dialog": "^1.1.15",
2525
"@radix-ui/react-dropdown-menu": "^2.1.16",
2626
"@radix-ui/react-label": "^2.1.7",
27+
"@radix-ui/react-popover": "^1.1.15",
2728
"@radix-ui/react-select": "^2.2.6",
2829
"@radix-ui/react-separator": "^1.1.7",
2930
"@radix-ui/react-slot": "^1.2.3",

pnpm-lock.yaml

Lines changed: 39 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)