Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
98 changes: 60 additions & 38 deletions app/admin/page.tsx
Original file line number Diff line number Diff line change
@@ -1,68 +1,82 @@
'use client'
"use client";

import { useState, useEffect } from 'react'
import { TeamHierarchyTree } from '@/components/TeamHierarchyTree'
import { useAuth } from '@/hooks/useAuth'
import { redirect, useSearchParams } from 'next/navigation'
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'
import { useOrganisation } from '@/components/organisation-switcher'
import { Building2 } from 'lucide-react'
import { useOrganisations } from '@/hooks/useOrganisations'
import { StakeholderManagement } from '@/components/StakeholderManagement'
import { useState, useEffect } from "react";
import { TeamHierarchyTree } from "@/components/TeamHierarchyTree";
import { useAuth } from "@/hooks/useAuth";
import { redirect, useSearchParams } from "next/navigation";
import {
Card,
CardContent,
CardDescription,
CardHeader,
CardTitle,
} from "@/components/ui/card";
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
import { useOrganisation } from "@/components/organisation-switcher";
import { Building2 } from "lucide-react";
import { useOrganisations } from "@/hooks/useOrganisations";
import { StakeholderManagement } from "@/components/StakeholderManagement";
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from "@/components/ui/select"
} from "@/components/ui/select";

export default function AdminPage() {
const { user, loading: authLoading, isAdmin } = useAuth()
const { selectedOrganisation } = useOrganisation()
const { organisations, loading: orgsLoading, fetchAllOrganisations } = useOrganisations()
const [organisationId, setOrganisationId] = useState<string | null>(null)
const searchParams = useSearchParams()
const tabParam = searchParams.get('tab')
const [activeTab, setActiveTab] = useState<string>('team-hierarchy')
const { user, loading: authLoading, isAdmin } = useAuth();
const { selectedOrganisation } = useOrganisation();
const {
organisations,
loading: orgsLoading,
fetchAllOrganisations,
} = useOrganisations();
const [organisationId, setOrganisationId] = useState<string | null>(null);
const searchParams = useSearchParams();
const tabParam = searchParams?.get("tab") ?? "team-hierarchy";
const [activeTab, setActiveTab] = useState<string>("team-hierarchy");

useEffect(() => {
// Fetch all organizations for admin view
if (isAdmin) {
fetchAllOrganisations().catch(error => {
console.error('Failed to fetch all organisations:', error)
})
fetchAllOrganisations().catch((error) => {
console.error("Failed to fetch all organisations:", error);
});
}
}, [isAdmin, fetchAllOrganisations])
}, [isAdmin, fetchAllOrganisations]);

useEffect(() => {
if (selectedOrganisation) {
setOrganisationId(selectedOrganisation.id)
setOrganisationId(selectedOrganisation.id);
}
}, [selectedOrganisation])
}, [selectedOrganisation]);

// Set the active tab based on the URL parameter
useEffect(() => {
if (tabParam) {
setActiveTab(tabParam)
setActiveTab(tabParam);
}
}, [tabParam])
}, [tabParam]);

if (authLoading || orgsLoading) {
return <div className="flex items-center justify-center min-h-screen">Loading...</div>
return (
<div className="flex items-center justify-center min-h-screen">
Loading...
</div>
);
}

// If user is not logged in, redirect to login page
if (!user) {
redirect('/login')
return null
redirect("/login");
return null;
}

// If user is not an admin, redirect to organization page
if (!isAdmin) {
redirect('/organisation')
return null
redirect("/organisation");
return null;
}

return (
Expand All @@ -71,14 +85,17 @@ export default function AdminPage() {
<h1 className="text-3xl font-bold">Admin Dashboard</h1>
<div className="flex items-center gap-4">
<Select
value={organisationId || ''}
value={organisationId || ""}
onValueChange={(value) => setOrganisationId(value)}
>
<SelectTrigger className="w-[250px]">
<SelectValue placeholder="Select organisation">
<div className="flex items-center gap-2">
<Building2 className="h-4 w-4" />
<span>{organisations?.find(org => org.id === organisationId)?.name || "Select organisation"}</span>
<span>
{organisations?.find((org) => org.id === organisationId)
?.name || "Select organisation"}
</span>
</div>
</SelectValue>
</SelectTrigger>
Expand All @@ -95,15 +112,20 @@ export default function AdminPage() {
</Select>
</div>
</div>

{!organisationId ? (
<Card>
<CardContent className="flex items-center justify-center min-h-[200px] text-muted-foreground">
Please select an organization to view and manage its settings
</CardContent>
</Card>
) : (
<Tabs defaultValue={activeTab} value={activeTab} onValueChange={setActiveTab} className="w-full">
<Tabs
defaultValue={activeTab}
value={activeTab}
onValueChange={setActiveTab}
className="w-full"
>
<TabsList className="mb-4">
<TabsTrigger value="team-hierarchy">Teams</TabsTrigger>
<TabsTrigger value="stakeholders">Stakeholders</TabsTrigger>
Expand Down Expand Up @@ -169,5 +191,5 @@ export default function AdminPage() {
</Tabs>
)}
</div>
)
}
);
}
69 changes: 40 additions & 29 deletions app/organisation/[organisationId]/decision/[id]/view/page.tsx
Original file line number Diff line number Diff line change
@@ -1,72 +1,83 @@
'use client'
"use client";

import { useParams } from 'next/navigation'
import { useDecision } from '@/hooks/useDecisions'
import { useStakeholders } from '@/hooks/useStakeholders'
import { DecisionSummary } from '@/components/decision-summary'
import Link from 'next/link'
import { useParams } from "next/navigation";
import { useDecision } from "@/hooks/useDecisions";
import { useStakeholders } from "@/hooks/useStakeholders";
import { DecisionSummary } from "@/components/decision-summary";
import Link from "next/link";

function PublishedBanner() {
return (
<div className="bg-sky-100 p-4 rounded-md">
<p className="text-slate-700">This decision has been published and can no longer be edited</p>
<p className="text-slate-700">
This decision has been published and can no longer be edited
</p>
</div>
)
);
}

function SupersededBanner({ supersedingDecisionId, supersedingDecisionTitle, organisationId }: {
supersedingDecisionId: string
supersedingDecisionTitle: string
organisationId: string
function SupersededBanner({
supersedingDecisionId,
supersedingDecisionTitle,
organisationId,
}: {
supersedingDecisionId: string;
supersedingDecisionTitle: string;
organisationId: string;
}) {
return (
<div className="bg-amber-100 p-4 rounded-md">
<p className="text-slate-700">
This decision has been superseded by{' '}
<Link
This decision has been superseded by{" "}
<Link
href={`/organisation/${organisationId}/decision/${supersedingDecisionId}/view`}
className="text-amber-800 hover:text-amber-900 underline"
>
{supersedingDecisionTitle}
</Link>
</p>
</div>
)
);
}

export default function DecisionView() {
const params = useParams()
const { decision, loading } = useDecision(params.id as string, params.organisationId as string)
const { stakeholders } = useStakeholders()
const params = useParams();
const { stakeholders } = useStakeholders();
const { decision, loading } = useDecision(
(params?.id as string) || "",
(params?.organisationId as string) || "",
);

if (!params?.id || !params?.organisationId) {
return <div>Invalid parameters</div>;
}

if (loading) {
return <div>Loading...</div>
return <div>Loading...</div>;
}

if (!decision) {
return <div>Decision not found</div>
return <div>Decision not found</div>;
}

const supersededByRelationship = decision.getSupersededByRelationship()
const supersededByRelationship = decision.getSupersededByRelationship();

return (
<div className="space-y-8">
<h1 className="text-2xl font-bold text-slate-900">{decision.title}</h1>

<DecisionSummary
decision={decision}
stakeholders={stakeholders}
/>
<DecisionSummary decision={decision} stakeholders={stakeholders} />

{decision.isPublished() && <PublishedBanner />}
{supersededByRelationship && (
<SupersededBanner
<SupersededBanner
supersedingDecisionId={supersededByRelationship.targetDecision.id}
supersedingDecisionTitle={supersededByRelationship.targetDecisionTitle}
supersedingDecisionTitle={
supersededByRelationship.targetDecisionTitle
}
organisationId={params.organisationId as string}
/>
)}
</div>
)
);
}

44 changes: 26 additions & 18 deletions app/organisation/[organisationId]/decision/create/page.tsx
Original file line number Diff line number Diff line change
@@ -1,28 +1,36 @@
'use client'
"use client";

import { useEffect, useRef } from 'react'
import { useRouter, useParams } from 'next/navigation'
import { useOrganisationDecisions } from '@/hooks/useOrganisationDecisions'
import { useAuth } from '@/hooks/useAuth'
import { useEffect, useRef } from "react";
import { useRouter, useParams } from "next/navigation";
import { useOrganisationDecisions } from "@/hooks/useOrganisationDecisions";
import { useAuth } from "@/hooks/useAuth";

export default function DecisionPage() {
const router = useRouter()
const params = useParams()
const organisationId = params.organisationId as string
const { createDecision } = useOrganisationDecisions(organisationId)
const { user } = useAuth()
const hasCreatedDecision = useRef(false)
const router = useRouter();
const params = useParams();
const { user } = useAuth();
const organisationId = (params?.organisationId as string) || "";
const { createDecision } = useOrganisationDecisions(organisationId);
const hasCreatedDecision = useRef(false);

useEffect(() => {
if (user) { // we need to wait until the logged in user is available
if (!hasCreatedDecision.current) { // we only want to run createDecision once
if (!params?.organisationId) return;

if (user) {
// we need to wait until the logged in user is available
if (!hasCreatedDecision.current) {
// we only want to run createDecision once
hasCreatedDecision.current = true;
createDecision().then((decision) => {
router.push(`${decision.id}/edit`)
})
router.push(`${decision.id}/edit`);
});
}
}
}, [user, createDecision, router])
}, [user, createDecision, router, params?.organisationId]);

if (!params?.organisationId) {
return <div>Invalid organisation ID</div>;
}

return <div>Creating decision...</div>
}
return <div>Creating decision...</div>;
}
Loading