Skip to content

Commit ebdfb17

Browse files
committed
Add promote
1 parent 6402b5b commit ebdfb17

File tree

38 files changed

+351
-201
lines changed

38 files changed

+351
-201
lines changed

src/app/(authenticated)/companies/[id]/connections/page.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ export default async function CompanyConnections({
2020
}
2121

2222
return (
23-
<div className="container m-auto h-full text-black">
23+
<div className="container m-auto h-full">
2424
<List title="Company connections">
2525
{connections.map((u) => (
2626
<UserTile key={u.id} user={u} />

src/app/(authenticated)/companies/[id]/page.tsx

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ import StandDetails from "./StandDetails";
1111
import { UserTile } from "@/components/user/UserTile";
1212
import { SessionTile } from "@/components/session";
1313
import EventDayButton from "@/components/EventDayButton";
14+
import { Scan } from "lucide-react";
15+
import Link from "next/link";
1416

1517
interface CompanyParams {
1618
id: string;
@@ -44,7 +46,7 @@ export default async function Company({ params }: { params: CompanyParams }) {
4446
const companyConnections = await CompanyService.getConnections(companyID);
4547

4648
return (
47-
<div className="container m-auto h-full text-black">
49+
<div className="container m-auto h-full">
4850
<div className="flex flex-col items-center gap-y-2 p-4 text-center">
4951
<h2 className="text-2xl font-bold">{company.name}</h2>
5052
<Image
@@ -61,6 +63,18 @@ export default async function Company({ params }: { params: CompanyParams }) {
6163
</span>
6264
)}
6365
</div>
66+
{/* Members section */}
67+
{user && isMember(user.role) && (
68+
<div className="flex justify-center items-center p-4 gap-2">
69+
<Link
70+
className="button button-primary text-sm flex-1"
71+
href={`/companies/${companyID}/promote`}
72+
>
73+
<Scan size={16} />
74+
Promote
75+
</Link>
76+
</div>
77+
)}
6478
{/* Days at the event */}
6579
{companyStands?.length ? (
6680
<GridList
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
"use client";
2+
3+
import ListCard from "@/components/ListCard";
4+
import MessageCard from "@/components/MessageCard";
5+
import QRCodeScanner from "@/components/QRCodeScanner";
6+
import { UserTile } from "@/components/user/UserTile";
7+
import { UserService } from "@/services/UserService";
8+
import { getUserFromQRCode } from "@/utils/utils";
9+
import { ReactNode, useEffect, useState } from "react";
10+
11+
interface CompanyPromoteScannerProps {
12+
cannonToken: string;
13+
company: Company;
14+
}
15+
16+
export default function CompanyPromoteScanner({
17+
cannonToken,
18+
company,
19+
}: CompanyPromoteScannerProps) {
20+
const [topCard, setTopCard] = useState<ReactNode | undefined>();
21+
const [bottomCard, setBottomCard] = useState<ReactNode | undefined>();
22+
const [statusCard, setStatusCard] = useState<ReactNode | undefined>();
23+
let cardsTimeout: NodeJS.Timeout;
24+
25+
async function handleQRCodeScanned(data: string) {
26+
const scannedUser = getUserFromQRCode(data);
27+
cardsTimeout && clearTimeout(cardsTimeout);
28+
29+
if (scannedUser) {
30+
setBottomCard(<UserTile user={scannedUser} />);
31+
if (
32+
await UserService.promote(cannonToken, scannedUser.id, {
33+
role: "company",
34+
company: {
35+
company: company.id,
36+
edition: "32", // FIXME: Use dynamic value
37+
},
38+
})
39+
) {
40+
setStatusCard(
41+
<MessageCard type="success" content="User successfully promoted!" />,
42+
);
43+
} else {
44+
setStatusCard(
45+
<MessageCard
46+
type="danger"
47+
content="Error on promoting user. Try again!"
48+
/>,
49+
);
50+
}
51+
} else {
52+
setBottomCard(<MessageCard type="danger" content="Invalid QR-Code" />);
53+
}
54+
55+
cardsTimeout = setTimeout(() => {
56+
setBottomCard(null);
57+
setStatusCard(null);
58+
}, 10 * 1000); // 10 seconds
59+
}
60+
61+
useEffect(() => {
62+
setBottomCard((card) => (
63+
<div className="flex flex-col justify-start gap-y-1">
64+
{statusCard}
65+
{card}
66+
</div>
67+
));
68+
}, [statusCard]);
69+
70+
useEffect(() => {
71+
setTopCard(
72+
<ListCard
73+
title={company.name}
74+
img={company.img}
75+
link={`/companies/${company.id}`}
76+
/>,
77+
);
78+
}, [company]);
79+
80+
return (
81+
<QRCodeScanner
82+
onQRCodeScanned={handleQRCodeScanned}
83+
topCard={topCard}
84+
bottomCard={bottomCard}
85+
/>
86+
);
87+
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import authOptions from "@/app/api/auth/[...nextauth]/authOptions";
2+
import { CompanyService } from "@/services/CompanyService";
3+
import { getServerSession } from "next-auth";
4+
import CompanyPromoteScanner from "./CompanyPromoteScanner";
5+
6+
interface CompanyPromoteParams {
7+
id: string;
8+
}
9+
10+
export default async function CompanyPromote({
11+
params,
12+
}: {
13+
params: CompanyPromoteParams;
14+
}) {
15+
const { id: companyID } = params;
16+
17+
const company = await CompanyService.getCompany(companyID);
18+
19+
if (!company) {
20+
return <div>Company not found</div>;
21+
}
22+
23+
const session = await getServerSession(authOptions);
24+
25+
return (
26+
<div className="container m-auto h-full">
27+
<CompanyPromoteScanner
28+
company={company}
29+
cannonToken={session!.cannonToken}
30+
/>
31+
</div>
32+
);
33+
}

src/app/(authenticated)/companies/page.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ export default async function Companies() {
1212
companies = companies.sort((a, b) => a.name.localeCompare(b.name));
1313

1414
return (
15-
<div className="container m-auto h-full text-black">
15+
<div className="container m-auto h-full">
1616
<CompaniesList companies={companies} />
1717
</div>
1818
);

src/app/(authenticated)/home/page.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ export default async function Home() {
3636
: [];
3737

3838
return (
39-
<div className="container m-auto h-full text-black">
39+
<div className="container m-auto h-full">
4040
{/* Upcoming Sessions */}
4141
<List title="Next Up" link="/schedule?day=today" linkText="See all">
4242
{upcomingSessions.length > 0 ? (

src/app/(authenticated)/layout.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,9 @@ export default async function AuthenticatedLayout({
1313
if (!session) redirect("/login");
1414

1515
return (
16-
<div className="h-screen bg-sinfo-gradient text-white flex flex-col">
16+
<div className="h-screen text-white flex flex-col">
1717
<Toolbar />
18-
<div className="flex-1 overflow-y-auto bg-gray-100 text-black">
18+
<div className="flex-1 overflow-y-scroll bg-gray-100 text-black mb-navbar">
1919
{children}
2020
</div>
2121
<BottomNavbar />

src/app/(authenticated)/profile/achievements/page.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,10 +28,10 @@ export default async function Achievements() {
2828
).sort() as AchievementKind[];
2929

3030
return (
31-
<div className="container m-auto h-full text-bold">
31+
<div className="container m-auto h-full">
3232
<div className="flex flex-col items-start gap-y-2 p-4 text-start text-sm">
3333
<h1 className="text-2xl font-bold">Achievements</h1>
34-
<span className="text-gray-500">
34+
<span className="text-gray-600">
3535
Total points:{" "}
3636
{user?.achievements?.reduce((acc, a) => acc + a.value, 0) || 0}
3737
</span>

src/app/(authenticated)/profile/connections/page.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ export default async function Connections() {
1313
}
1414

1515
return (
16-
<div className="container m-auto h-full text-black">
16+
<div className="container m-auto h-full">
1717
<List title="Connections">
1818
{user.connections.map((u) => (
1919
<UserTile key={u.id} user={u} />

src/app/(authenticated)/profile/page.tsx

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@ import { isCompany } from "@/utils/utils";
1212
import { Award, UserPen } from "lucide-react";
1313
import { getServerSession } from "next-auth";
1414
import Link from "next/link";
15-
import { Suspense } from "react";
1615

1716
const N_ACHIEVEMENTS = 5;
1817
const N_CONNECTIONS = 3;
@@ -26,7 +25,7 @@ export default async function Profile() {
2625
}
2726

2827
return (
29-
<div className="container m-auto h-full text-black">
28+
<div className="container m-auto h-full">
3029
<ProfileHeader user={user} />
3130
<div className="px-4 py-2">
3231
<Link
@@ -44,9 +43,7 @@ export default async function Profile() {
4443
title="Curriculum Vitae (CV)"
4544
description="Submit your CV and get the chance to win a prize"
4645
>
47-
<Suspense fallback={<div>Loading</div>}>
48-
<CurriculumVitae session={session} user={user} currentUser />
49-
</Suspense>
46+
<CurriculumVitae session={session} user={user} currentUser />
5047
</List>
5148
)}
5249

@@ -89,6 +86,7 @@ export default async function Profile() {
8986
subtitle="Click here to know more"
9087
link="/profile/achievements"
9188
icon={Award}
89+
extraClassName="!bg-sinfo-primary !text-white"
9290
/>
9391
)}
9492
</GridList>

0 commit comments

Comments
 (0)