Skip to content

Commit ca5378e

Browse files
continue a dev les entreprise et ajouter les fonctionalitée pas encore m
1 parent 36dc85a commit ca5378e

File tree

4 files changed

+189
-93
lines changed

4 files changed

+189
-93
lines changed

src/app/companies/page.tsx

Lines changed: 115 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -1,85 +1,129 @@
1-
'use client';
2-
3-
import { useEffect, useState, useCallback } from 'react';
4-
import { getCompanies, Company } from '@/lib/actions/companies';
1+
import { getCompaniesForUserDashboard, ManagedCompany, InvestedCompany, OtherCompany } from '@/lib/actions/companies';
52
import { Card, CardHeader, CardTitle, CardDescription, CardContent } from "@/components/ui/card";
63
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table";
74
import { Badge } from "@/components/ui/badge";
85
import { CreateCompanyDialog } from '@/components/create-company-dialog';
96
import { Button } from '@/components/ui/button';
107
import Link from 'next/link';
11-
import { Loader2 } from 'lucide-react';
12-
13-
export default function CompaniesPage() {
14-
const [companies, setCompanies] = useState<Company[]>([]);
15-
const [loading, setLoading] = useState(true);
8+
import { InvestDialog } from '@/components/invest-dialog';
9+
import type { CompanyWithDetails } from '@/lib/actions/companies';
1610

17-
const fetchCompanies = useCallback(async () => {
18-
setLoading(true);
19-
const companiesData = await getCompanies();
20-
setCompanies(companiesData);
21-
setLoading(false);
22-
}, []);
2311

24-
useEffect(() => {
25-
fetchCompanies();
26-
}, [fetchCompanies]);
12+
function CompanyTableRow({ company, type }: { company: any, type: 'managed' | 'invested' | 'other' }) {
13+
return (
14+
<TableRow>
15+
<TableCell>
16+
<div className="font-medium">{company.name}</div>
17+
<div className="text-sm text-muted-foreground">{company.industry}</div>
18+
</TableCell>
19+
{type === 'managed' && (
20+
<TableCell>
21+
<Badge variant="secondary">{company.role.toUpperCase()}</Badge>
22+
</TableCell>
23+
)}
24+
{type === 'invested' && (
25+
<>
26+
<TableCell className="font-mono">{company.sharesHeld.toFixed(4)}</TableCell>
27+
<TableCell className="font-mono">${company.sharesValue.toFixed(2)}</TableCell>
28+
</>
29+
)}
30+
{(type === 'managed' || type === 'other') && (
31+
<TableCell className="font-mono">${company.cash.toFixed(2)}</TableCell>
32+
)}
33+
{type === 'other' && (
34+
<TableCell className="font-mono">${company.marketCap.toLocaleString(undefined, { maximumFractionDigits: 0})}</TableCell>
35+
)}
36+
<TableCell className="text-right space-x-2">
37+
<Button asChild variant="outline" size="sm">
38+
<Link href={`/companies/${company.id}`}>Détails</Link>
39+
</Button>
40+
{type === 'other' && (
41+
<InvestDialog company={company as CompanyWithDetails}>
42+
<Button size="sm">Investir</Button>
43+
</InvestDialog>
44+
)}
45+
</TableCell>
46+
</TableRow>
47+
)
48+
}
2749

28-
if (loading) {
50+
function CompanyTable({ title, description, companies, type }: { title: string, description: string, companies: any[], type: 'managed' | 'invested' | 'other' }) {
51+
const headers = {
52+
managed: ["Entreprise", "Mon Rôle", "Trésorerie", ""],
53+
invested: ["Entreprise", "Parts Détenues", "Valeur des Parts", ""],
54+
other: ["Entreprise", "Trésorerie", "Cap. Boursière", ""]
55+
}
56+
2957
return (
30-
<div className="flex h-64 items-center justify-center">
31-
<Loader2 className="h-8 w-8 animate-spin" />
32-
</div>
58+
<Card>
59+
<CardHeader>
60+
<CardTitle>{title}</CardTitle>
61+
<CardDescription>{description}</CardDescription>
62+
</CardHeader>
63+
<CardContent>
64+
<Table>
65+
<TableHeader>
66+
<TableRow>
67+
{headers[type].map((header, i) => (
68+
<TableHead key={i} className={i === headers[type].length - 1 ? 'text-right' : ''}>{header}</TableHead>
69+
))}
70+
</TableRow>
71+
</TableHeader>
72+
<TableBody>
73+
{companies.length > 0 ? (
74+
companies.map((company) => <CompanyTableRow key={company.id} company={company} type={type} />)
75+
) : (
76+
<TableRow>
77+
<TableCell colSpan={headers[type].length} className="h-24 text-center text-muted-foreground">
78+
{type === 'managed' ? "Vous ne gérez aucune entreprise." : type === 'invested' ? "Vous n'avez investi dans aucune entreprise." : "Aucune autre entreprise disponible."}
79+
</TableCell>
80+
</TableRow>
81+
)}
82+
</TableBody>
83+
</Table>
84+
</CardContent>
85+
</Card>
3386
)
34-
}
87+
}
3588

36-
return (
37-
<Card>
38-
<CardHeader className="flex-row items-center justify-between">
39-
<div>
40-
<CardTitle>Entreprises</CardTitle>
41-
<CardDescription>Créez ou investissez dans des entreprises, influencez la gestion et gagnez des dividendes.</CardDescription>
42-
</div>
43-
<CreateCompanyDialog onCompanyCreated={fetchCompanies} />
44-
</CardHeader>
45-
<CardContent>
46-
<Table>
47-
<TableHeader>
48-
<TableRow>
49-
<TableHead>Entreprise</TableHead>
50-
<TableHead>Industrie</TableHead>
51-
<TableHead>Trésorerie</TableHead>
52-
<TableHead className="text-right">Actions</TableHead>
53-
</TableRow>
54-
</TableHeader>
55-
<TableBody>
56-
{companies.length > 0 ? (
57-
companies.map((company) => (
58-
<TableRow key={company.id}>
59-
<TableCell>
60-
<div className="font-medium">{company.name}</div>
61-
</TableCell>
62-
<TableCell>
63-
<Badge variant="secondary">{company.industry}</Badge>
64-
</TableCell>
65-
<TableCell className="font-mono">${parseFloat(company.cash).toFixed(2)}</TableCell>
66-
<TableCell className="text-right space-x-2">
67-
<Button asChild variant="outline" size="sm">
68-
<Link href={`/companies/${company.id}`}>Détails</Link>
69-
</Button>
70-
</TableCell>
71-
</TableRow>
72-
))
73-
) : (
74-
<TableRow>
75-
<TableCell colSpan={4} className="h-24 text-center text-muted-foreground">
76-
Aucune entreprise n'a encore été créée. Soyez le premier !
77-
</TableCell>
78-
</TableRow>
89+
90+
export default async function CompaniesPage() {
91+
const { managedCompanies, investedCompanies, otherCompanies } = await getCompaniesForUserDashboard();
92+
93+
return (
94+
<div className="space-y-6">
95+
<div className="flex items-center justify-between">
96+
<div>
97+
<h1 className="text-2xl font-bold tracking-tight">Espace Entreprises</h1>
98+
<p className="text-muted-foreground">Créez, gérez et investissez dans des entreprises dirigées par des joueurs.</p>
99+
</div>
100+
<CreateCompanyDialog />
101+
</div>
102+
103+
{managedCompanies.length > 0 && (
104+
<CompanyTable
105+
title="Mes Entreprises (Dirigeant)"
106+
description="Les entreprises que vous gérez directement."
107+
companies={managedCompanies}
108+
type="managed"
109+
/>
79110
)}
80-
</TableBody>
81-
</Table>
82-
</CardContent>
83-
</Card>
84-
);
111+
112+
{investedCompanies.length > 0 && (
113+
<CompanyTable
114+
title="Mes Investissements"
115+
description="Les entreprises dans lesquelles vous détenez des parts."
116+
companies={investedCompanies}
117+
type="invested"
118+
/>
119+
)}
120+
121+
<CompanyTable
122+
title="Marché des Entreprises"
123+
description="Toutes les entreprises disponibles à l'investissement."
124+
companies={otherCompanies}
125+
type="other"
126+
/>
127+
</div>
128+
);
85129
}

src/components/create-company-dialog.tsx

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@ import {
1717
import {
1818
Form,
1919
FormControl,
20-
FormDescription,
2120
FormField,
2221
FormItem,
2322
FormLabel,
@@ -29,21 +28,19 @@ import { useToast } from '@/hooks/use-toast';
2928
import { createCompany } from '@/lib/actions/companies';
3029
import { Loader2 } from 'lucide-react';
3130
import { usePortfolio } from '@/context/portfolio-context';
31+
import { useRouter } from 'next/navigation';
3232

3333
const companyFormSchema = z.object({
3434
name: z.string().min(3, "Le nom doit faire au moins 3 caractères.").max(50, "Le nom ne doit pas dépasser 50 caractères."),
3535
industry: z.string().min(3, "L'industrie doit faire au moins 3 caractères.").max(50, "L'industrie ne doit pas dépasser 50 caractères."),
3636
description: z.string().min(10, "La description doit faire au moins 10 caractères.").max(200, "La description ne doit pas dépasser 200 caractères."),
3737
});
3838

39-
interface CreateCompanyDialogProps {
40-
onCompanyCreated: () => void;
41-
}
42-
43-
export function CreateCompanyDialog({ onCompanyCreated }: CreateCompanyDialogProps) {
39+
export function CreateCompanyDialog() {
4440
const [open, setOpen] = useState(false);
4541
const { toast } = useToast();
4642
const { cash } = usePortfolio();
43+
const router = useRouter();
4744
const creationCost = 1000;
4845

4946
const form = useForm<z.infer<typeof companyFormSchema>>({
@@ -64,7 +61,7 @@ export function CreateCompanyDialog({ onCompanyCreated }: CreateCompanyDialogPro
6461
toast({ title: "Succès", description: result.success });
6562
setOpen(false);
6663
form.reset();
67-
onCompanyCreated();
64+
router.refresh();
6865
}
6966
}
7067

@@ -80,7 +77,7 @@ export function CreateCompanyDialog({ onCompanyCreated }: CreateCompanyDialogPro
8077
<DialogHeader>
8178
<DialogTitle>Lancer une Nouvelle Entreprise</DialogTitle>
8279
<DialogDescription>
83-
La création d'une entreprise coûte {creationCost.toLocaleString()}$. Cette somme constituera sa trésorerie initiale.
80+
La création d'une entreprise coûte ${creationCost.toLocaleString()}. Cette somme constituera sa trésorerie initiale.
8481
Vous serez nommé PDG.
8582
</DialogDescription>
8683
</DialogHeader>

src/components/manage-company-assets-dialog.tsx

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import { useToast } from '@/hooks/use-toast';
1515
import { Loader2 } from 'lucide-react';
1616
import { buyAssetForCompany, sellAssetForCompany } from '@/lib/actions/companies';
1717
import type { CompanyWithDetails } from '@/lib/actions/companies';
18+
import { useRouter } from 'next/navigation';
1819

1920
interface ManageCompanyAssetsDialogProps {
2021
company: CompanyWithDetails;
@@ -34,8 +35,9 @@ const sellFormSchema = z.object({
3435
export function ManageCompanyAssetsDialog({ company, children }: ManageCompanyAssetsDialogProps) {
3536
const [open, setOpen] = useState(false);
3637
const [activeTab, setActiveTab] = useState("buy");
37-
const { assets, getAssetByTicker, refreshData } = useMarketData();
38+
const { assets, getAssetByTicker } = useMarketData();
3839
const { toast } = useToast();
40+
const router = useRouter();
3941

4042
const buyForm = useForm<z.infer<typeof buyFormSchema>>({
4143
resolver: zodResolver(buyFormSchema),
@@ -70,7 +72,7 @@ export function ManageCompanyAssetsDialog({ company, children }: ManageCompanyAs
7072
toast({ variant: 'destructive', title: 'Erreur', description: result.error });
7173
} else if (result.success) {
7274
toast({ title: 'Succès', description: result.success });
73-
await refreshData();
75+
router.refresh();
7476
setOpen(false);
7577
}
7678
}
@@ -89,7 +91,7 @@ export function ManageCompanyAssetsDialog({ company, children }: ManageCompanyAs
8991
toast({ variant: 'destructive', title: 'Erreur', description: result.error });
9092
} else if (result.success) {
9193
toast({ title: 'Succès', description: result.success });
92-
await refreshData();
94+
router.refresh();
9395
setOpen(false);
9496
}
9597
}

src/lib/actions/companies.ts

Lines changed: 64 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import { db } from '@/lib/db';
55
import { companies, companyMembers, users, companyShares, companyHoldings, assets as assetsSchema } from '@/lib/db/schema';
66
import { getSession } from '../session';
77
import { revalidatePath } from 'next/cache';
8-
import { eq, and } from 'drizzle-orm';
8+
import { eq, and, desc } from 'drizzle-orm';
99
import { updatePriceFromTrade } from './assets';
1010

1111
const createCompanySchema = z.object({
@@ -74,19 +74,72 @@ export async function createCompany(values: z.infer<typeof createCompanySchema>)
7474
}
7575
}
7676

77-
export async function getCompanies() {
78-
try {
79-
const allCompanies = await db.query.companies.findMany({
80-
orderBy: (companies, { desc }) => [desc(companies.createdAt)],
81-
});
82-
return allCompanies;
83-
} catch (error) {
84-
console.error("Error fetching companies:", error);
85-
return [];
77+
export async function getCompaniesForUserDashboard() {
78+
const session = await getSession();
79+
80+
const allCompanies = await db.query.companies.findMany({
81+
orderBy: (companies, { desc }) => [desc(companies.createdAt)],
82+
});
83+
84+
const companiesWithMarketData = allCompanies.map(company => {
85+
const cash = parseFloat(company.cash);
86+
const totalShares = parseFloat(company.totalShares);
87+
const sharePrice = parseFloat(company.sharePrice);
88+
const marketCap = totalShares * sharePrice;
89+
90+
return {
91+
...company,
92+
cash: cash,
93+
marketCap: marketCap,
94+
sharePrice: sharePrice,
95+
totalShares: totalShares,
96+
}
97+
});
98+
99+
100+
if (!session?.id) {
101+
return {
102+
managedCompanies: [],
103+
investedCompanies: [],
104+
otherCompanies: companiesWithMarketData,
105+
};
86106
}
107+
108+
const [userMemberships, userShares] = await Promise.all([
109+
db.query.companyMembers.findMany({ where: eq(companyMembers.userId, session.id) }),
110+
db.query.companyShares.findMany({ where: eq(companyShares.userId, session.id) }),
111+
]);
112+
113+
const managedCompanyIds = new Set(userMemberships.map(m => m.companyId));
114+
const investedCompanyIds = new Set(userShares.map(s => s.companyId));
115+
116+
const managedCompanies: any[] = [];
117+
const investedCompanies: any[] = [];
118+
const otherCompanies: any[] = [];
119+
120+
companiesWithMarketData.forEach(company => {
121+
if (managedCompanyIds.has(company.id)) {
122+
const membership = userMemberships.find(m => m.companyId === company.id);
123+
managedCompanies.push({ ...company, role: membership?.role });
124+
} else if (investedCompanyIds.has(company.id)) {
125+
const share = userShares.find(s => s.companyId === company.id);
126+
const sharesHeld = parseFloat(share?.quantity || '0');
127+
investedCompanies.push({
128+
...company,
129+
sharesHeld: sharesHeld,
130+
sharesValue: sharesHeld * company.sharePrice,
131+
});
132+
} else {
133+
otherCompanies.push(company);
134+
}
135+
});
136+
137+
return { managedCompanies, investedCompanies, otherCompanies };
87138
}
88139

89-
export type Company = Awaited<ReturnType<typeof getCompanies>>[0];
140+
export type ManagedCompany = Awaited<ReturnType<typeof getCompaniesForUserDashboard>>['managedCompanies'][0];
141+
export type InvestedCompany = Awaited<ReturnType<typeof getCompaniesForUserDashboard>>['investedCompanies'][0];
142+
export type OtherCompany = Awaited<ReturnType<typeof getCompaniesForUserDashboard>>['otherCompanies'][0];
90143

91144

92145
export async function getCompanyById(companyId: number) {

0 commit comments

Comments
 (0)