Skip to content

Commit 5e24a6d

Browse files
committed
Fix homepage container flexibility
1 parent 4479525 commit 5e24a6d

File tree

14 files changed

+262
-14301
lines changed

14 files changed

+262
-14301
lines changed

frontend/package.json

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@
1515
"test:unit": "tsc --noEmit && NODE_OPTIONS='--experimental-vm-modules --no-warnings=DEP0040' jest"
1616
},
1717
"dependencies": {
18-
"@apollo/client": "^3.13.9",
1918
"@fortawesome/fontawesome-svg-core": "^6.7.2",
2019
"@fortawesome/free-brands-svg-icons": "^6.7.2",
2120
"@fortawesome/free-regular-svg-icons": "^6.7.2",
@@ -45,7 +44,6 @@
4544
"dompurify": "^3.2.6",
4645
"eslint-plugin-import": "^2.32.0",
4746
"framer-motion": "^12.23.12",
48-
"graphql": "^16.11.0",
4947
"leaflet": "^1.9.4",
5048
"leaflet.markercluster": "^1.5.3",
5149
"lodash": "^4.17.21",
@@ -59,8 +57,7 @@
5957
"react-dom": "^19.1.1",
6058
"react-icons": "^5.5.0",
6159
"react-router-dom": "^7.8.0",
62-
"tailwind-merge": "^3.3.1",
63-
"tailwindcss-animate": "^1.0.7"
60+
"tailwind-merge": "^3.3.1"
6461
},
6562
"devDependencies": {
6663
"@axe-core/react": "^4.10.2",
@@ -69,7 +66,7 @@
6966
"@playwright/test": "^1.54.2",
7067
"@swc/core": "^1.13.3",
7168
"@swc/jest": "^0.2.39",
72-
"@tailwindcss/postcss": "^4.1.11",
69+
"@tailwindcss/postcss": "^4.1.12",
7370
"@testing-library/jest-dom": "^6.6.4",
7471
"@testing-library/react": "^16.3.0",
7572
"@types/jest": "^29.5.14",
@@ -103,7 +100,8 @@
103100
"prettier": "^3.6.2",
104101
"prettier-plugin-tailwindcss": "^0.6.14",
105102
"require-in-the-middle": "^7.5.2",
106-
"tailwindcss": "^3.4.17",
103+
"tailwindcss": "^4.1.12",
104+
"tailwindcss-animate": "^1.0.7",
107105
"ts-jest": "^29.4.1",
108106
"ts-node": "^10.9.2",
109107
"typescript": "~5.8.3",

frontend/pnpm-lock.yaml

Lines changed: 0 additions & 13459 deletions
This file was deleted.

frontend/postcss.config.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
module.exports = {
22
plugins: {
3+
"@tailwindcss/postcss": {},
34
autoprefixer: {},
4-
tailwindcss: {},
55
},
66
}

frontend/src/app/about/layout.tsx

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,16 @@
1-
import { Metadata } from 'next'
2-
import React from 'react'
3-
import { getStaticMetadata } from 'utils/metaconfig'
4-
5-
export const metadata: Metadata = getStaticMetadata('about', '/about')
6-
7-
export default function AboutLayout({ children }: { children: React.ReactNode }) {
8-
return children
1+
export default function RootLayout({
2+
children,
3+
}: {
4+
children: React.ReactNode
5+
}) {
6+
return (
7+
<html lang="en">
8+
<body>
9+
<nav style={{ padding: "10px", background: "#eee" }}>
10+
<a href="/">Home</a> | <a href="/about">About</a>
11+
</nav>
12+
<main>{children}</main>
13+
</body>
14+
</html>
15+
)
916
}

frontend/src/app/about/page.tsx

Lines changed: 8 additions & 282 deletions
Original file line numberDiff line numberDiff line change
@@ -1,285 +1,11 @@
1-
'use client'
2-
import { useQuery } from '@apollo/client'
3-
import {
4-
faCircleCheck,
5-
faClock,
6-
faUserGear,
7-
faMapSigns,
8-
faScroll,
9-
faUsers,
10-
faTools,
11-
faArrowUpRightFromSquare,
12-
} from '@fortawesome/free-solid-svg-icons'
13-
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
14-
import { Tooltip } from '@heroui/tooltip'
15-
import upperFirst from 'lodash/upperFirst'
16-
import Image from 'next/image'
17-
import Link from 'next/link'
18-
import { useRouter } from 'next/navigation'
19-
import { useEffect, useState } from 'react'
20-
import FontAwesomeIconWrapper from 'wrappers/FontAwesomeIconWrapper'
21-
import { ErrorDisplay, handleAppError } from 'app/global-error'
22-
import { GET_PROJECT_METADATA, GET_TOP_CONTRIBUTORS } from 'server/queries/projectQueries'
23-
import { GET_LEADER_DATA } from 'server/queries/userQueries'
24-
import type { Contributor } from 'types/contributor'
25-
import type { Project } from 'types/project'
26-
import type { User } from 'types/user'
27-
import { aboutText, technologies } from 'utils/aboutData'
28-
import AnchorTitle from 'components/AnchorTitle'
29-
import AnimatedCounter from 'components/AnimatedCounter'
30-
import LoadingSpinner from 'components/LoadingSpinner'
31-
import Markdown from 'components/MarkdownWrapper'
32-
import SecondaryCard from 'components/SecondaryCard'
33-
import TopContributorsList from 'components/TopContributorsList'
34-
import UserCard from 'components/UserCard'
35-
36-
const leaders = {
37-
arkid15r: 'CCSP, CISSP, CSSLP',
38-
kasya: 'CC',
39-
mamicidal: 'CISSP',
40-
}
41-
const projectKey = 'nest'
42-
43-
const About = () => {
44-
const { data: projectMetadataResponse, error: projectMetadataRequestError } = useQuery(
45-
GET_PROJECT_METADATA,
46-
{
47-
variables: { key: projectKey },
48-
}
49-
)
50-
51-
const { data: topContributorsResponse, error: topContributorsRequestError } = useQuery(
52-
GET_TOP_CONTRIBUTORS,
53-
{
54-
variables: {
55-
excludedUsernames: Object.keys(leaders),
56-
hasFullName: true,
57-
key: projectKey,
58-
limit: 24,
59-
},
60-
}
61-
)
62-
63-
const [projectMetadata, setProjectMetadata] = useState<Project | null>(null)
64-
const [topContributors, setTopContributors] = useState<Contributor[]>([])
65-
66-
useEffect(() => {
67-
if (projectMetadataResponse?.project) {
68-
setProjectMetadata(projectMetadataResponse.project)
69-
}
70-
71-
if (projectMetadataRequestError) {
72-
handleAppError(projectMetadataRequestError)
73-
}
74-
}, [projectMetadataResponse, projectMetadataRequestError])
75-
76-
useEffect(() => {
77-
if (topContributorsResponse?.topContributors) {
78-
setTopContributors(topContributorsResponse.topContributors)
79-
}
80-
81-
if (topContributorsRequestError) {
82-
handleAppError(topContributorsRequestError)
83-
}
84-
}, [topContributorsResponse, topContributorsRequestError])
85-
86-
const isLoading =
87-
!projectMetadataResponse ||
88-
!topContributorsResponse ||
89-
(projectMetadataRequestError && !projectMetadata) ||
90-
(topContributorsRequestError && !topContributors)
91-
92-
if (isLoading) {
93-
return <LoadingSpinner />
94-
}
95-
96-
if (!projectMetadata || !topContributors) {
97-
return (
98-
<ErrorDisplay
99-
statusCode={404}
100-
title="Data not found"
101-
message="Sorry, the page you're looking for doesn't exist"
102-
/>
103-
)
104-
}
105-
1+
export default function AboutPage() {
1062
return (
107-
<div className="min-h-screen p-8 text-gray-600 dark:bg-[#212529] dark:text-gray-300">
108-
<div className="mx-auto max-w-6xl">
109-
<h1 className="mb-6 mt-4 text-4xl font-bold">About</h1>
110-
<SecondaryCard icon={faScroll} title={<AnchorTitle title="History" />}>
111-
{aboutText.map((text) => (
112-
<div key={text} className="mb-4">
113-
<div key={text}>
114-
<Markdown content={text} />
115-
</div>
116-
</div>
117-
))}
118-
</SecondaryCard>
119-
120-
<SecondaryCard icon={faArrowUpRightFromSquare} title={<AnchorTitle title="Leaders" />}>
121-
<div className="flex w-full flex-col items-center justify-around overflow-hidden md:flex-row">
122-
{Object.keys(leaders).map((username) => (
123-
<div key={username}>
124-
<LeaderData username={username} />
125-
</div>
126-
))}
127-
</div>
128-
</SecondaryCard>
129-
130-
{topContributors && (
131-
<TopContributorsList
132-
contributors={topContributors}
133-
icon={faUsers}
134-
maxInitialDisplay={12}
135-
/>
136-
)}
137-
138-
<SecondaryCard icon={faTools} title={<AnchorTitle title="Technologies & Tools" />}>
139-
<div className="w-full">
140-
<div className="grid w-full grid-cols-1 justify-center sm:grid-cols-2 lg:grid-cols-4 lg:pl-8">
141-
{technologies.map((tech) => (
142-
<div key={tech.section} className="mb-2">
143-
<h3 className="mb-3 font-semibold text-blue-400">{tech.section}</h3>
144-
<ul className="space-y-3">
145-
{Object.entries(tech.tools).map(([name, details]) => (
146-
<li key={name} className="flex flex-row items-center gap-2">
147-
<Image
148-
alt={`${name} icon`}
149-
className="inline-block align-middle grayscale"
150-
height={24}
151-
src={details.icon}
152-
width={24}
153-
/>
154-
<Link
155-
href={details.url}
156-
className="text-gray-600 hover:underline dark:text-gray-300"
157-
target="_blank"
158-
rel="noopener noreferrer"
159-
>
160-
{upperFirst(name)}
161-
</Link>
162-
</li>
163-
))}
164-
</ul>
165-
</div>
166-
))}
167-
</div>
168-
</div>
169-
</SecondaryCard>
170-
171-
{projectMetadata.recentMilestones.length > 0 && (
172-
<SecondaryCard icon={faMapSigns} title={<AnchorTitle title="Roadmap" />}>
173-
<div className="grid gap-4">
174-
{[...projectMetadata.recentMilestones]
175-
.filter((milestone) => milestone.state !== 'closed')
176-
.sort((a, b) => (a.title > b.title ? 1 : -1))
177-
.map((milestone, index) => (
178-
<div
179-
key={milestone.url || milestone.title || index}
180-
className="flex items-center gap-4 overflow-hidden rounded-lg bg-gray-200 p-6 dark:bg-gray-700"
181-
>
182-
<div className="flex-1">
183-
<Link
184-
href={milestone.url}
185-
target="_blank"
186-
rel="noopener noreferrer"
187-
className="inline-block"
188-
>
189-
<h3 className="mb-2 text-xl font-semibold text-blue-400">
190-
{milestone.title}
191-
<Tooltip
192-
closeDelay={100}
193-
content={
194-
milestone.progress === 100
195-
? 'Completed'
196-
: milestone.progress > 0
197-
? 'In Progress'
198-
: 'Not Started'
199-
}
200-
id={`tooltip-state-${index}`}
201-
delay={100}
202-
placement="top"
203-
showArrow
204-
>
205-
<span className="ml-4 inline-block text-gray-400">
206-
<FontAwesomeIcon
207-
icon={
208-
milestone.progress === 100
209-
? faCircleCheck
210-
: milestone.progress > 0
211-
? faUserGear
212-
: faClock
213-
}
214-
/>
215-
</span>
216-
</Tooltip>
217-
</h3>
218-
</Link>
219-
<p className="text-gray-600 dark:text-gray-300">{milestone.body}</p>
220-
</div>
221-
</div>
222-
))}
223-
</div>
224-
</SecondaryCard>
225-
)}
226-
227-
<div className="grid gap-6 md:grid-cols-4">
228-
{[
229-
{ label: 'Forks', value: projectMetadata.forksCount },
230-
{ label: 'Stars', value: projectMetadata.starsCount },
231-
{ label: 'Contributors', value: projectMetadata.contributorsCount },
232-
{ label: 'Open Issues', value: projectMetadata.issuesCount },
233-
].map((stat, index) => (
234-
<div key={index}>
235-
<SecondaryCard className="text-center">
236-
<div className="mb-2 text-3xl font-bold text-blue-400">
237-
<AnimatedCounter end={Math.floor(stat.value / 10) * 10} duration={2} />+
238-
</div>
239-
<div className="text-gray-600 dark:text-gray-300">{stat.label}</div>
240-
</SecondaryCard>
241-
</div>
242-
))}
243-
</div>
244-
</div>
3+
<div style={{ padding: "2rem", fontFamily: "sans-serif" }}>
4+
<h1>About OWASP Nest</h1>
5+
<p>
6+
OWASP Nest is a platform to discover, engage, and contribute to OWASP
7+
projects and initiatives. It serves as a central hub for the community.
8+
</p>
2459
</div>
246-
)
247-
}
248-
249-
const LeaderData = ({ username }: { username: string }) => {
250-
const { data, loading, error } = useQuery(GET_LEADER_DATA, {
251-
variables: { key: username },
252-
})
253-
const router = useRouter()
254-
255-
if (loading) return <p>Loading {username}...</p>
256-
if (error) return <p>Error loading {username}'s data</p>
257-
258-
const user = data?.user
259-
260-
if (!user) {
261-
return <p>No data available for {username}</p>
262-
}
263-
264-
const handleButtonClick = (user: User) => {
265-
router.push(`/members/${user.login}`)
266-
}
267-
268-
return (
269-
<UserCard
270-
avatar={user.avatarUrl}
271-
button={{
272-
icon: <FontAwesomeIconWrapper icon="fa-solid fa-right-to-bracket" />,
273-
label: 'View Profile',
274-
onclick: () => handleButtonClick(user),
275-
}}
276-
className="h-64 w-40 bg-inherit"
277-
company={user.company}
278-
description={leaders[user.login]}
279-
location={user.location}
280-
name={user.name || username}
281-
/>
282-
)
10+
);
28311
}
284-
285-
export default About

0 commit comments

Comments
 (0)