mirror of
https://github.com/luxfhe/research-ocp-fhe.git
synced 2026-11-03 06:05:38 +00:00
225 lines
7.3 KiB
TypeScript
225 lines
7.3 KiB
TypeScript
'use client';
|
|
|
|
import { useState, useEffect } from 'react';
|
|
import { Button } from '@/components/ui/button';
|
|
import { Alert, AlertDescription } from '@/components/ui/alert';
|
|
import { LockIcon, UnlockIcon, Plus, AlertCircle } from 'lucide-react';
|
|
import { useRole } from '@/hooks/use-role';
|
|
import { useCompany } from '@/hooks/use-company';
|
|
import { useQueryClient } from '@tanstack/react-query';
|
|
import { useAccount } from '@/hooks/wagmi-viem-proxy/use-account';
|
|
import dynamic from 'next/dynamic';
|
|
import { Round } from '@/lib/types/company';
|
|
|
|
const DynamicCreateRoundModal = dynamic(
|
|
() =>
|
|
import('@/components/create-round-modal').then(mod => mod.CreateRoundModal),
|
|
{
|
|
ssr: false,
|
|
}
|
|
);
|
|
|
|
const DynamicRoundCard = dynamic(
|
|
() => import('@/components/round-card').then(mod => mod.RoundCard),
|
|
{
|
|
ssr: false,
|
|
}
|
|
);
|
|
|
|
const DynamicOwnershipTable = dynamic(
|
|
() => import('@/components/ownership-table').then(mod => mod.OwnershipTable),
|
|
{
|
|
ssr: false,
|
|
}
|
|
);
|
|
|
|
export default function Page({
|
|
params,
|
|
}: {
|
|
params: Promise<{ slug: string }>;
|
|
}) {
|
|
const [companyId, setCompanyId] = useState<string>('');
|
|
const [showCreateModal, setShowCreateModal] = useState(false);
|
|
const { role } = useRole();
|
|
const { address: walletAddress } = useAccount();
|
|
const queryClient = useQueryClient();
|
|
|
|
// Get company data from API
|
|
const { data: company, isLoading, error } = useCompany(companyId);
|
|
|
|
// Check if current user is the company owner
|
|
const isCompanyOwner =
|
|
walletAddress && company?.founder
|
|
? walletAddress.toLowerCase() === company.founder.toLowerCase()
|
|
: false;
|
|
|
|
// Determine if user should have founder privileges
|
|
const hasFounderPrivileges = role === 'FOUNDER' && isCompanyOwner;
|
|
|
|
useEffect(() => {
|
|
const loadCompanyId = async () => {
|
|
const resolvedParams = await params;
|
|
setCompanyId(resolvedParams.slug); // slug is actually the company ID (Ethereum address)
|
|
};
|
|
|
|
loadCompanyId();
|
|
}, [params]);
|
|
|
|
const handleCreateRound = () => {
|
|
// Refresh company data after successful round creation
|
|
// The modal will handle the API call and close itself
|
|
// We just need to trigger a refetch of the company data
|
|
queryClient.invalidateQueries({ queryKey: ['company', companyId] });
|
|
};
|
|
|
|
// Sort rounds by date (most recent first)
|
|
const sortedRounds =
|
|
company?.rounds?.toSorted(
|
|
(a, b) => new Date(b.date).getTime() - new Date(a.date).getTime()
|
|
) || [];
|
|
|
|
return (
|
|
<div className="min-h-screen bg-zinc-50 text-zinc-900">
|
|
<main className="max-w-6xl mx-auto px-4 py-6 space-y-6">
|
|
{/* Disclaimer Alert for non-owner founders */}
|
|
{role === 'FOUNDER' &&
|
|
!isLoading &&
|
|
!error &&
|
|
company &&
|
|
!isCompanyOwner && (
|
|
<Alert variant="destructive" className="mb-6">
|
|
<AlertCircle className="h-4 w-4" />
|
|
<AlertDescription>
|
|
You are viewing this company as a founder, but you are not the
|
|
owner of this company. Your access is limited to public
|
|
information only. You cannot create new rounds or decrypt
|
|
confidential data.
|
|
</AlertDescription>
|
|
</Alert>
|
|
)}
|
|
|
|
{/* Header */}
|
|
<div className="mb-8">
|
|
<h1 className="text-3xl font-bold mb-2 text-zinc-900">
|
|
{company?.name || companyId} Company
|
|
</h1>
|
|
<p className="text-zinc-600 mb-4">
|
|
Rounds & Allocations for {company?.name || companyId}
|
|
</p>
|
|
|
|
{/* Create Round Button - Only visible for company owner founders */}
|
|
{hasFounderPrivileges && !isLoading && !error && company && (
|
|
<Button
|
|
onClick={() => setShowCreateModal(true)}
|
|
className="flex items-center gap-2"
|
|
>
|
|
<Plus className="h-4 w-4" />
|
|
Create New Round
|
|
</Button>
|
|
)}
|
|
</div>
|
|
|
|
{/* Content */}
|
|
{isLoading ? (
|
|
// Show skeleton loading cards
|
|
<div className="space-y-6">
|
|
<DynamicRoundCard
|
|
isLoading
|
|
companyAddress={companyId}
|
|
isCompanyOwner={false}
|
|
/>
|
|
<DynamicRoundCard
|
|
isLoading
|
|
companyAddress={companyId}
|
|
isCompanyOwner={false}
|
|
/>
|
|
</div>
|
|
) : error ? (
|
|
// Error state
|
|
<div className="text-center py-12">
|
|
<div className="flex flex-col items-center gap-4">
|
|
<AlertCircle className="h-12 w-12 text-red-500" />
|
|
<div>
|
|
<h3 className="text-lg font-semibold text-zinc-900 mb-2">
|
|
Company Not Found
|
|
</h3>
|
|
<p className="text-zinc-600">
|
|
{error.message.includes('Company not found')
|
|
? `No company found with ID "${companyId}"`
|
|
: 'Failed to load company data. Please try again.'}
|
|
</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
) : company ? (
|
|
// Show company data with ownership table and rounds
|
|
<div className="space-y-8">
|
|
{/* Ownership Table */}
|
|
<DynamicOwnershipTable
|
|
company={company}
|
|
companyAddress={company.contractAddress || companyId}
|
|
/>
|
|
|
|
{/* Rounds Section */}
|
|
{sortedRounds.length > 0 ? (
|
|
<div className="space-y-6">
|
|
<h2 className="text-xl font-semibold text-zinc-900">
|
|
Investment Rounds
|
|
</h2>
|
|
{sortedRounds.map((round: Round) => (
|
|
<DynamicRoundCard
|
|
key={round.id}
|
|
round={round}
|
|
companyAddress={company?.contractAddress || companyId}
|
|
isCompanyOwner={isCompanyOwner}
|
|
/>
|
|
))}
|
|
</div>
|
|
) : (
|
|
<div className="text-center py-12">
|
|
<p className="text-zinc-600">
|
|
No rounds found for this company.
|
|
</p>
|
|
{hasFounderPrivileges && (
|
|
<Button
|
|
onClick={() => setShowCreateModal(true)}
|
|
className="mt-4 flex items-center gap-2 mx-auto"
|
|
variant="outline"
|
|
>
|
|
<Plus className="h-4 w-4" />
|
|
Create First Round
|
|
</Button>
|
|
)}
|
|
</div>
|
|
)}
|
|
</div>
|
|
) : null}
|
|
|
|
{/* Legend */}
|
|
{!isLoading && !error && sortedRounds.length > 0 && (
|
|
<section className="text-xs text-zinc-600 flex items-center gap-4">
|
|
<span className="inline-flex items-center gap-1">
|
|
<LockIcon className="h-3 w-3" /> Encrypted
|
|
</span>
|
|
<span className="inline-flex items-center gap-1">
|
|
<UnlockIcon className="h-3 w-3 text-emerald-600" /> Decrypted
|
|
(local)
|
|
</span>
|
|
</section>
|
|
)}
|
|
</main>
|
|
|
|
{/* Create Round Modal */}
|
|
{company && (
|
|
<DynamicCreateRoundModal
|
|
open={showCreateModal}
|
|
onOpenChange={setShowCreateModal}
|
|
onCreateRound={handleCreateRound}
|
|
companyId={companyId}
|
|
contractAddress={company.contractAddress}
|
|
/>
|
|
)}
|
|
</div>
|
|
);
|
|
}
|