Skip to content
17 changes: 7 additions & 10 deletions src/app/dashboard/dashboard-content.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
import { useState } from "react";
import type * as React from "react";
import { cn } from "@/components/lib/utils";
import SelectCoachingRelationship from "@/components/ui/dashboard/select-coaching-relationship";
import CoachingSessionList from "@/components/ui/dashboard/coaching-session-list";
import AddEntities from "@/components/ui/dashboard/add-entities";
import { CoachingSessionDialog } from "@/components/ui/dashboard/coaching-session-dialog";
Expand All @@ -20,8 +19,6 @@ function DashboardContainer({
"p-4",
// Mobile: stack vertically
"flex flex-col gap-6",
// Tablet and up (640px+): side by side
"sm:grid sm:grid-cols-2",
// Never grow wider than the site-header
"max-w-screen-2xl",
// Ensure full width for children
Expand All @@ -35,7 +32,9 @@ function DashboardContainer({

export function DashboardContent() {
const [dialogOpen, setDialogOpen] = useState(false);
const [sessionToEdit, setSessionToEdit] = useState<CoachingSession | undefined>();
const [sessionToEdit, setSessionToEdit] = useState<
CoachingSession | undefined
>();

const handleOpenDialog = (session?: CoachingSession) => {
setSessionToEdit(session);
Expand All @@ -49,13 +48,11 @@ export function DashboardContent() {

return (
<>
<div className="p-4 max-w-screen-2xl">
<div className="mb-8 w-full">
<AddEntities onCreateSession={() => handleOpenDialog()} />
</div>
</div>
<DashboardContainer>
<SelectCoachingRelationship />
<AddEntities
className="mb-8"
onCreateSession={() => handleOpenDialog()}
/>
<CoachingSessionList onUpdateSession={handleOpenDialog} />
</DashboardContainer>

Expand Down
55 changes: 28 additions & 27 deletions src/components/ui/coaching-relationship-selector.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,10 @@ import { useEffect } from "react";
import { useAuthStore } from "@/lib/providers/auth-store-provider";
import { useCoachingRelationshipStateStore } from "@/lib/providers/coaching-relationship-state-store-provider";
import { useCoachingSessionStateStore } from "@/lib/providers/coaching-session-state-store-provider";
import { cn } from "../lib/utils";

interface CoachingRelationshipsSelectorProps extends PopoverProps {
className?: string;
/// The Organization's Id for which to get a list of associated CoachingRelationships
organizationId: Id;
/// Disable the component from interaction with the user
Expand All @@ -38,18 +40,13 @@ function CoachingRelationshipsSelectItems({
// Be sure to cache the list of current coaching relationships in the CoachingRelationshipStateStore
useEffect(() => {
if (!relationships.length) return;
console.debug(
`relationships (useEffect): ${JSON.stringify(relationships)}`
);
setCurrentCoachingRelationships(relationships);
}, [relationships, setCurrentCoachingRelationships]);

if (isLoading) return <div>Loading...</div>;
if (isError) return <div>Error loading coaching relationships</div>;
if (!relationships?.length) return <div>No coaching relationships found</div>;

console.debug(`relationships: ${JSON.stringify(relationships)}`);

return (
<>
{relationships.map((rel) => (
Expand All @@ -63,6 +60,7 @@ function CoachingRelationshipsSelectItems({
}

export default function CoachingRelationshipSelector({
className,
organizationId,
disabled,
onSelect,
Expand Down Expand Up @@ -101,29 +99,32 @@ export default function CoachingRelationshipSelector({
? getCurrentCoachingRelationship(currentCoachingRelationshipId)
: null;

const displayValue = currentRelationship ? (
<>
{currentRelationship.coach_first_name}{" "}
{currentRelationship.coach_last_name} -&gt;{" "}
{currentRelationship.coachee_first_name}{" "}
{currentRelationship.coachee_last_name}
</>
) : undefined;
const displayValue =
currentRelationship && currentRelationship.id ? (
<>
{currentRelationship.coach_first_name}{" "}
{currentRelationship.coach_last_name} -&gt;{" "}
{currentRelationship.coachee_first_name}{" "}
{currentRelationship.coachee_last_name}
</>
) : undefined;

return (
<Select
disabled={disabled}
value={currentCoachingRelationshipId ?? undefined}
onValueChange={handleSetCoachingRelationship}
>
<SelectTrigger id="coaching-relationship-selector">
<SelectValue placeholder="Select coaching relationship">
{displayValue}
</SelectValue>
</SelectTrigger>
<SelectContent>
<CoachingRelationshipsSelectItems organizationId={organizationId} />
</SelectContent>
</Select>
<div className={cn("font-normal", className)}>
<Select
disabled={disabled}
value={currentCoachingRelationshipId ?? undefined}
onValueChange={handleSetCoachingRelationship}
>
<SelectTrigger id="coaching-relationship-selector">
<SelectValue placeholder="Select coaching relationship">
{displayValue}
</SelectValue>
</SelectTrigger>
<SelectContent>
<CoachingRelationshipsSelectItems organizationId={organizationId} />
</SelectContent>
</Select>
</div>
);
}
4 changes: 2 additions & 2 deletions src/components/ui/coaching-session.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -62,11 +62,11 @@ const CoachingSession: React.FC<CoachingSessionProps> = ({
</DropdownMenuTrigger>
<DropdownMenuContent align="end">
<DropdownMenuItem onClick={onUpdate}>
Update Session
Edit
</DropdownMenuItem>
{isCoach && (
<DropdownMenuItem onClick={onDelete} className="text-destructive">
Delete Session
Delete
</DropdownMenuItem>
)}
</DropdownMenuContent>
Expand Down
11 changes: 8 additions & 3 deletions src/components/ui/dashboard/add-entities.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,17 @@ import { AddMemberButton } from "./add-member-button";
import { useRouter } from "next/navigation";
import { useOrganizationStateStore } from "@/lib/providers/organization-state-store-provider";
import { useAuthStore } from "@/lib/providers/auth-store-provider";
import { cn } from "@/components/lib/utils";

interface AddEntitiesProps {
className?: string;
onCreateSession: () => void;
}

export default function AddEntities({ onCreateSession }: AddEntitiesProps) {
export default function AddEntities({
className,
onCreateSession,
}: AddEntitiesProps) {
const router = useRouter();
const { currentOrganizationId } = useOrganizationStateStore((state) => state);
const { isCoach } = useAuthStore((state) => state);
Expand All @@ -20,7 +25,7 @@ export default function AddEntities({ onCreateSession }: AddEntitiesProps) {
};

return (
<div className="space-y-4">
<div className={cn("space-y-4", className)}>
<h3 className="text-xl sm:text-2xl font-semibold tracking-tight">
Add New
</h3>
Expand All @@ -37,4 +42,4 @@ export default function AddEntities({ onCreateSession }: AddEntitiesProps) {
</div>
</div>
);
}
}
168 changes: 109 additions & 59 deletions src/components/ui/dashboard/coaching-session-list.tsx
Original file line number Diff line number Diff line change
@@ -1,22 +1,31 @@
"use client";

import { useState } from "react";
import { Button } from "@/components/ui/button";
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
import { ArrowUpDown } from "lucide-react";
import { useCoachingRelationshipStateStore } from "@/lib/providers/coaching-relationship-state-store-provider";
import { useCoachingSessionList } from "@/lib/api/coaching-sessions";
import { useCoachingSessionMutation } from "@/lib/api/coaching-sessions";
import { CoachingSession as CoachingSessionComponent } from "@/components/ui/coaching-session";
import { DateTime } from "ts-luxon";
import type { CoachingSession } from "@/types/coaching-session";
import { Id } from "@/types/general";
import {
filterAndSortCoachingSessions,
type CoachingSession,
} from "@/types/coaching-session";
import { Id, SortOrder } from "@/types/general";
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
import CoachingRelationshipSelector from "../coaching-relationship-selector";
import { useOrganizationStateStore } from "@/lib/providers/organization-state-store-provider";
import { cn } from "@/components/lib/utils";

interface CoachingSessionListProps {
className?: string;
onUpdateSession: (session: CoachingSession) => void;
}

export default function CoachingSessionList({ onUpdateSession }: CoachingSessionListProps) {
export default function CoachingSessionList({
className,
onUpdateSession,
}: CoachingSessionListProps) {
const { currentOrganizationId } = useOrganizationStateStore((state) => state);
const { currentCoachingRelationshipId } = useCoachingRelationshipStateStore(
(state) => state
);
Expand Down Expand Up @@ -47,66 +56,107 @@ export default function CoachingSessionList({ onUpdateSession }: CoachingSession
}
};

const [sortByDate, setSortByDate] = useState(true);
const upcomingSessions = coachingSessions
? filterAndSortCoachingSessions(coachingSessions, SortOrder.Ascending, true)
: [];

const sortedSessions = coachingSessions
? [...coachingSessions].sort((a, b) => {
return new Date(b.date).getTime() - new Date(a.date).getTime();
})
const previousSessions = coachingSessions
? filterAndSortCoachingSessions(
coachingSessions,
SortOrder.Descending,
false
)
: [];

if (isLoadingCoachingSessions) return <div>Loading coaching sessions...</div>;
if (isErrorCoachingSessions)
return <div>Error loading coaching sessions</div>;
let loadingCoachingSessions = (
<div className="flex items-center justify-center py-8">
<p className="text-lg text-muted-foreground">
Loading your coaching sessions...
</p>
</div>
);

let noCoachingSessions = (
<div className="flex items-center justify-center py-8">
<p className="text-lg text-muted-foreground">
Select a coaching relationship to view your coaching sessions.
</p>
</div>
);

let errorLoadingCoachingSessions = (
<div className="flex items-center justify-center py-8">
<p className="text-lg font-bold">
There was an error trying to load your coaching sessions.
</p>
</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>
</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>
<Card className={cn("min-w-96", className)}>
<CardHeader>
<CardTitle>
<div className="flex justify-between flex-col lg:flex-row">
<div>Coaching Sessions</div>
<CoachingRelationshipSelector
className="pt-4 lg:min-w-64"
organizationId={currentOrganizationId}
disabled={!currentOrganizationId}
/>
</div>
</CardTitle>
</CardHeader>
<CardContent>
{!currentCoachingRelationshipId ? (
<div className="flex items-center justify-center py-8">
<p className="text-lg text-muted-foreground">
Choose a Relationship to view Coaching Sessions
</p>
</div>
) : (
<div className="space-y-4">
{sortedSessions.map((coachingSession) => (
<CoachingSessionComponent
key={coachingSession.id}
coachingSession={coachingSession}
onUpdate={() => onUpdateSession(coachingSession)}
onDelete={() => handleDeleteCoachingSession(coachingSession.id)}
/>
))}
</div>
)}
<Tabs defaultValue="upcoming" className="w-full items-start">
<TabsList className="grid w-full grid-cols-2">
<TabsTrigger value="upcoming">Upcoming</TabsTrigger>
<TabsTrigger value="previous">Previous</TabsTrigger>
</TabsList>
<TabsContent value="upcoming" className="mt-4">
{isLoadingCoachingSessions ? (
loadingCoachingSessions
) : isErrorCoachingSessions ? (
errorLoadingCoachingSessions
) : !currentCoachingRelationshipId ? (
noCoachingSessions
) : (
<div className="space-y-4">
{upcomingSessions.map((coachingSession) => (
<CoachingSessionComponent
key={coachingSession.id}
coachingSession={coachingSession}
onUpdate={() => onUpdateSession(coachingSession)}
onDelete={() =>
handleDeleteCoachingSession(coachingSession.id)
}
/>
))}
</div>
)}
</TabsContent>
<TabsContent value="previous" className="mt-4">
{isLoadingCoachingSessions ? (
loadingCoachingSessions
) : isErrorCoachingSessions ? (
errorLoadingCoachingSessions
) : !currentCoachingRelationshipId ? (
noCoachingSessions
) : (
<div className="space-y-4">
{previousSessions.map((coachingSession) => (
<CoachingSessionComponent
key={coachingSession.id}
coachingSession={coachingSession}
onUpdate={() => onUpdateSession(coachingSession)}
onDelete={() =>
handleDeleteCoachingSession(coachingSession.id)
}
/>
))}
</div>
)}
</TabsContent>
</Tabs>
</CardContent>
</Card>
);
Expand Down
Loading