-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
split choosing relationship and coaching sessions
- Loading branch information
1 parent
a0f6206
commit 703fe78
Showing
9 changed files
with
287 additions
and
149 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,37 +1,38 @@ | ||
import { Metadata } from "next"; | ||
|
||
import * as React from "react"; | ||
|
||
import { cn } from "@/lib/utils"; | ||
|
||
import SelectCoachingSession from "@/components/ui/dashboard/select-coaching-session"; | ||
import type { Metadata } from "next" | ||
import type * as React from "react" | ||
import { cn } from "@/lib/utils" | ||
import SelectCoachingRelationship from "@/components/ui/dashboard/select-coaching-relationship" | ||
import CoachingSessionList from "@/components/ui/dashboard/coaching-session-list" | ||
|
||
export const metadata: Metadata = { | ||
title: "Dashboard", | ||
description: "Coaching dashboard", | ||
}; | ||
} | ||
|
||
function DashboardContainer({ | ||
className, | ||
...props | ||
}: React.HTMLAttributes<HTMLDivElement>) { | ||
function DashboardContainer({ className, ...props }: React.HTMLAttributes<HTMLDivElement>) { | ||
return ( | ||
<div | ||
className={cn( | ||
"flex items-center justify-center [&>div]:w-full", | ||
className | ||
// Base styles | ||
"p-4", | ||
// Mobile: stack vertically | ||
"flex flex-col gap-6", | ||
// Tablet and up (640px+): side by side | ||
"sm:grid sm:grid-cols-2", | ||
// Ensure full width for children | ||
"[&>*]:w-full", | ||
className, | ||
)} | ||
{...props} | ||
/> | ||
); | ||
) | ||
} | ||
|
||
export default function DashboardPage() { | ||
return ( | ||
<div className="items-start justify-center gap-6 rounded-lg p-8 md:grid lg:grid-cols-2 xl:grid-cols-3"> | ||
<DashboardContainer> | ||
<SelectCoachingSession /> | ||
</DashboardContainer> | ||
</div> | ||
); | ||
<DashboardContainer> | ||
<SelectCoachingRelationship /> | ||
<CoachingSessionList /> | ||
</DashboardContainer> | ||
) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,71 @@ | ||
"use client" | ||
|
||
import React from 'react'; | ||
import { format } from 'date-fns'; | ||
import { Card, CardHeader } from '@/components/ui/card'; | ||
import { Button } from '@/components/ui/button'; | ||
import Link from 'next/link'; | ||
import { useCoachingSessionStateStore } from "@/lib/providers/coaching-session-state-store-provider"; | ||
import { useOverarchingGoalByCoachingSessionId } from "@/lib/api/overarching-goals"; | ||
import { Id } from '@/types/general'; | ||
|
||
interface CoachingSessionProps { | ||
coachingSession: { | ||
id: Id; | ||
date: string; | ||
}; | ||
} | ||
|
||
const CoachingSession: React.FC<CoachingSessionProps> = ({ coachingSession }) => { | ||
const { setCurrentCoachingSessionId } = useCoachingSessionStateStore((state) => state); | ||
|
||
return ( | ||
<Card> | ||
<CardHeader className="p-4"> | ||
<div className="flex flex-col sm:flex-row sm:items-center justify-between gap-2"> | ||
<div className="space-y-1"> | ||
<OverarchingGoal coachingSessionId={coachingSession.id} /> | ||
<div className="text-sm text-muted-foreground"> | ||
{format(new Date(coachingSession.date), "MMMM d, yyyy h:mm a")} | ||
</div> | ||
</div> | ||
<Link href={`/coaching-sessions/${coachingSession.id}`} passHref> | ||
<Button | ||
size="sm" | ||
className="w-full sm:w-auto mt-2 sm:mt-0" | ||
onClick={() => setCurrentCoachingSessionId(coachingSession.id)} | ||
> | ||
Join Session | ||
</Button> | ||
</Link> | ||
</div> | ||
</CardHeader> | ||
</Card> | ||
); | ||
}; | ||
|
||
interface OverarchingGoalProps { | ||
coachingSessionId: Id; | ||
} | ||
|
||
const OverarchingGoal: React.FC<OverarchingGoalProps> = ({ coachingSessionId }) => { | ||
const { | ||
overarchingGoal, | ||
isLoading: isLoadingOverarchingGoal, | ||
isError: isErrorOverarchingGoal, | ||
} = useOverarchingGoalByCoachingSessionId(coachingSessionId); | ||
|
||
let titleText: string; | ||
|
||
if (isLoadingOverarchingGoal) { | ||
titleText = "Loading..."; | ||
} else if (isErrorOverarchingGoal) { | ||
titleText = "Error loading Overarching Goal"; | ||
} else { | ||
titleText = overarchingGoal?.title || "No goal set"; | ||
} | ||
|
||
return <div>{titleText}</div>; | ||
}; | ||
|
||
export default CoachingSession; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,123 @@ | ||
"use client" | ||
|
||
import { useState, useEffect, useCallback } from "react" | ||
import { Button } from "@/components/ui/button" | ||
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card" | ||
import { ArrowUpDown, CalendarPlus } from "lucide-react" | ||
import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogTrigger } from "@/components/ui/dialog" | ||
import { Input } from "@/components/ui/input" | ||
import { Label } from "@/components/ui/label" | ||
import CoachingSession from "@/components/ui/coaching-session" | ||
import { useCoachingRelationshipStateStore } from "@/lib/providers/coaching-relationship-state-store-provider" | ||
import { useCoachingSessionStateStore } from "@/lib/providers/coaching-session-state-store-provider"; | ||
import { createCoachingSession, useCoachingSessions } from "@/lib/api/coaching-sessions"; | ||
|
||
|
||
export default function CoachingSessionList() { | ||
const { currentCoachingRelationshipId } = useCoachingRelationshipStateStore((state) => state) | ||
const { | ||
coachingSessions, | ||
isLoading: isLoadingCoachingSessions, | ||
isError: isErrorCoachingSessions, | ||
} = useCoachingSessions(currentCoachingRelationshipId); | ||
const { setCurrentCoachingSessions } = useCoachingSessionStateStore( | ||
(state) => state | ||
); | ||
|
||
const [sortByDate, setSortByDate] = useState(true) | ||
const [isDialogOpen, setIsDialogOpen] = useState(false) | ||
const [newSessionDate, setNewSessionDate] = useState("") | ||
|
||
useEffect(() => { | ||
if (!coachingSessions.length) return; | ||
setCurrentCoachingSessions(coachingSessions); | ||
}, [coachingSessions]); | ||
|
||
const handleCreateSession = (e: React.FormEvent) => { | ||
e.preventDefault() | ||
createCoachingSession(currentCoachingRelationshipId, newSessionDate).then((createdCoachingSession) => { | ||
setIsDialogOpen(false) | ||
setNewSessionDate("") | ||
setCurrentCoachingSessions([...coachingSessions, createdCoachingSession]) | ||
}).catch((err) => { | ||
console.error("Failed to create new Coaching Session: " + err); | ||
throw err; | ||
}); | ||
} | ||
|
||
const sortedSessions = [...coachingSessions].sort((a, b) => { | ||
return new Date(b.date).getTime() - new Date(a.date).getTime() | ||
}) | ||
|
||
if (!currentCoachingRelationshipId) return <div>Choose a Coaching Relationship to View Coaching Sessions</div> | ||
if (isLoadingCoachingSessions) return <div>Loading coaching sessions...</div> | ||
if (isErrorCoachingSessions) return <div>Error loading coaching sessions</div> | ||
|
||
return ( | ||
<Card className="flex-1"> | ||
<CardHeader className="space-y-4"> | ||
<div className="flex flex-col sm:flex-row items-start sm:items-center justify-between gap-4"> | ||
<CardTitle className="text-xl sm:text-2xl">Coaching Sessions</CardTitle> | ||
<Dialog open={isDialogOpen} onOpenChange={setIsDialogOpen}> | ||
<DialogTrigger asChild> | ||
<Button | ||
variant="outline" | ||
size="sm" | ||
className="w-full sm:w-auto" | ||
disabled={!currentCoachingRelationshipId} | ||
> | ||
<CalendarPlus className="mr-2 h-4 w-4" /> | ||
Create New Coaching Session | ||
</Button> | ||
</DialogTrigger> | ||
<DialogContent> | ||
<DialogHeader> | ||
<DialogTitle>Create New Coaching Session</DialogTitle> | ||
</DialogHeader> | ||
<form onSubmit={handleCreateSession} className="space-y-4"> | ||
<div className="space-y-2"> | ||
<Label htmlFor="session-date">Session Date and Time</Label> | ||
<Input | ||
id="session-date" | ||
type="datetime-local" | ||
required | ||
value={newSessionDate} | ||
onChange={(e) => setNewSessionDate(e.target.value)} | ||
/> | ||
</div> | ||
<Button type="submit">Create Session</Button> | ||
</form> | ||
</DialogContent> | ||
</Dialog> | ||
</div> | ||
<div className="flex flex-col sm:flex-row items-start sm:items-center justify-between gap-2"> | ||
<Button | ||
variant="ghost" | ||
size="sm" | ||
className="text-muted-foreground w-full sm:w-auto justify-between" | ||
onClick={() => setSortByDate(true)} | ||
> | ||
<span>Date and Time</span> | ||
<ArrowUpDown className="ml-2 h-4 w-4" /> | ||
</Button> | ||
<Button | ||
variant="ghost" | ||
size="sm" | ||
className="text-muted-foreground w-full sm:w-auto justify-between" | ||
onClick={() => setSortByDate(false)} | ||
> | ||
<span>Overarching Goal</span> | ||
<ArrowUpDown className="ml-2 h-4 w-4" /> | ||
</Button> | ||
</div> | ||
</CardHeader> | ||
<CardContent> | ||
<div className="space-y-4"> | ||
{sortedSessions.map((coachingSession) => ( | ||
<CoachingSession key={coachingSession.id} coachingSession={coachingSession} /> | ||
))} | ||
</div> | ||
</CardContent> | ||
</Card> | ||
) | ||
} |
35 changes: 35 additions & 0 deletions
35
src/components/ui/dashboard/select-coaching-relationship.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
"use client" | ||
|
||
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card" | ||
import { Label } from "@/components/ui/label" | ||
import { useAuthStore } from "@/lib/providers/auth-store-provider" | ||
import OrganizationSelector from "../organization-selector" | ||
import CoachingRelationshipSelector from "../coaching-relationship-selector" | ||
import { useOrganizationStateStore } from "@/lib/providers/organization-state-store-provider" | ||
import { useCoachingRelationshipStateStore } from "@/lib/providers/coaching-relationship-state-store-provider" | ||
|
||
export default function SelectCoachingRelationship() { | ||
const { userId } = useAuthStore((state) => state) | ||
const { currentOrganizationId } = useOrganizationStateStore((state) => state) | ||
const { currentCoachingRelationshipId } = useCoachingRelationshipStateStore((state) => state) | ||
|
||
return ( | ||
<Card className="w-full"> | ||
<CardHeader className="space-y-2"> | ||
<CardTitle className="text-xl sm:text-2xl">Choose a Coaching Relationship</CardTitle> | ||
<CardDescription className="text-sm">Select current organization and relationship</CardDescription> | ||
</CardHeader> | ||
<CardContent className="space-y-6"> | ||
<div className="space-y-2"> | ||
<Label htmlFor="organization">Organization</Label> | ||
<OrganizationSelector userId={userId} /> | ||
</div> | ||
|
||
<div className="space-y-2"> | ||
<Label htmlFor="relationship">Relationship</Label> | ||
<CoachingRelationshipSelector organizationId={currentOrganizationId} disabled={!currentOrganizationId} /> | ||
</div> | ||
</CardContent> | ||
</Card> | ||
) | ||
} |
Oops, something went wrong.