Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat: workday api within about page #22

Merged
merged 28 commits into from
Dec 19, 2024
Merged
Show file tree
Hide file tree
Changes from 17 commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
72ca4e5
chore: add swr and cors libs for fetching and middleware
hetd54 Aug 2, 2024
e1830e0
feat: middleware to post req to workday
hetd54 Aug 2, 2024
98b1a18
feat: workday data on about page
hetd54 Aug 2, 2024
4febae6
Merge branch 'main' into feat-workday-api
hetd54 Nov 25, 2024
4e89709
refactor: app routing
hetd54 Nov 25, 2024
26d79fa
refactor: api route for workday api
hetd54 Nov 25, 2024
763d046
fix: requestoptions typing
hetd54 Nov 25, 2024
075b03b
Merge branch 'main' into feat-workday-api
hetd54 Nov 26, 2024
c860637
style: match existing site
hetd54 Nov 26, 2024
cd58219
refactor: LinkCard with block link
hetd54 Nov 26, 2024
830b63d
style: more responsive
hetd54 Nov 26, 2024
f896d45
feat: add link to section for scroll
hetd54 Nov 26, 2024
7ab4f77
chore: remove unused deps
hetd54 Nov 26, 2024
07c2943
deps: update next
hetd54 Dec 2, 2024
ecaff23
refactor: main in layout
hetd54 Dec 2, 2024
296d237
Merge branch 'main' into feat-workday-api
hetd54 Dec 3, 2024
f318f6e
Merge branch 'main' into feat-workday-api
hetd54 Dec 5, 2024
e93df46
chore: remove extra divs
hetd54 Dec 10, 2024
29304cc
feat: spinner
hetd54 Dec 10, 2024
db8858e
feat: stream fetched data to client component
hetd54 Dec 17, 2024
e0c9ae5
feat: SectionHeader component
hetd54 Dec 17, 2024
a1a3045
Merge branch 'main' into feat-workday-api
hetd54 Dec 17, 2024
ae13bb3
refactor: stream event data to subcomponents
hetd54 Dec 17, 2024
508f86b
fix: add back monthly cal view
hetd54 Dec 17, 2024
4b6ddc8
chore: return error if unable to fetch
hetd54 Dec 17, 2024
4a2c824
chore: remove unused hooks
hetd54 Dec 17, 2024
a618c91
fix: appease the linter
hetd54 Dec 17, 2024
065cd57
refactor: fragments for components
hetd54 Dec 18, 2024
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
8 changes: 7 additions & 1 deletion app/about/page.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
import { Opportunities } from "@/components/Opportunities"

export default function About() {
return <div>About</div>
return (
<div>
<Opportunities />
</div>
hetd54 marked this conversation as resolved.
Show resolved Hide resolved
)
}
27 changes: 27 additions & 0 deletions app/api/about/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
export async function POST() {
const myHeaders = new Headers()
myHeaders.append("Content-Type", "application/json")
myHeaders.append(
"Cookie",
"PLAY_SESSION=9365d69619d226b22f74256e51894a0476197a1f-instance=vps-prod-tvvtdnej.prod-vps.pr501.cust.pdx.wd; __cf_bm=zYtKNPBpg6veqmLc2qdk_TeHEGdGWIcSRG9tdtkQNbQ-1721323439-1.0.1.1-TApbCV1fQKTtEauZKvZ5XnXrZQJL7dcML6ixA3AycoiBMROa.iAzMq_K.5E7iaZfCj9.WhQQlmYfAxW8wdwZgQ; __cflb=02DiuHJZe28xXz6hQKLf1exjNbMDM5uxekNnt7kFV7LUC; _cfuvid=hqIGrDcFihah5EiFO0c_HEM4gIPwkeWCOxjMxNqjR20-1721323439159-0.0.1.1-604800000; wd-browser-id=fc0f0b1d-4547-488d-b353-99c751c61dae; wday_vps_cookie=1122671626.53810.0000"
)

const raw = JSON.stringify({
limit: 20,
offset: 0,
appliedFacets: {},
searchText: "180 George Street",
})

const response = await fetch(
"https://brown.wd5.myworkdayjobs.com/wday/cxs/brown/staff-careers-brown/jobs",
{
method: "POST",
headers: myHeaders,
body: raw,
}
)
const json = await response.json()

return Response.json({ json })
}
4 changes: 3 additions & 1 deletion app/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,9 @@ export default function RootLayout({
<html lang="en">
<body className={inter.className}>
<Navbar />
<main>{children}</main>
<main className="bg-white flex min-h-screen flex-col p-0 lg:p-24">
{children}
</main>
<Footer />
</body>
</html>
Expand Down
4 changes: 2 additions & 2 deletions app/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ import EventSection from "@/components/EventSection"

export default function Home() {
return (
<main className="bg-white flex min-h-screen flex-col items-center justify-between p-0 lg:p-24">
<div>
<EventSection />
</main>
</div>
hetd54 marked this conversation as resolved.
Show resolved Hide resolved
)
}
19 changes: 19 additions & 0 deletions components/LinkCard.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import React from "react"

type CardProps = {
className?: string
}

export function LinkCard({
className = "",
children,
}: React.PropsWithChildren<CardProps>) {
return (
<section
className={`"h-full p-6 flex flex-col gap-2 rounded-lg drop-shadow-md hover:drop-shadow-lg transition duration-500" ${className ? className : "bg-gray-50 border-white border-2"}`}
/* https://design-system.w3.org/components/cards.html#block-link-cards */ data-component="card"
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👀
Does this work? I thought you needed some JS in there too to make this work.... BIG if true 🤔

>
{children}
</section>
)
}
106 changes: 106 additions & 0 deletions components/Opportunities.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
"use client"
import React, { useState, useEffect } from "react"
import Link from "next/link"
import { LinkCard } from "@/components/LinkCard"
import {
MapPinIcon,
ArrowRightIcon,
UserPlusIcon,
} from "@heroicons/react/24/solid"

interface OpportunityProps {
total: number
jobPostings: PositionProps[]
facets: Object[]
userAuthenticated: boolean
}

interface PositionProps {
title: string
externalPath: string
locationsText: string
postedOn: string
bulletFields: string[]
}

export const Opportunities: React.FC = () => {
const initialData = {
total: 0,
jobPostings: [],
userAuthenticated: false,
facets: [],
}
const [data, setData] = useState<OpportunityProps>(initialData)
const [isLoading, setLoading] = useState(true)
const [error, setError] = useState(null)

const handleApiCall = async () => {
try {
const res = await fetch("/api/about", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
})

if (!res.ok) {
throw new Error(`Failed to fetch: ${res.status}`)
}

const opportunities = await res.json()
setData(opportunities.json)
} catch (err: any) {
setError(err.message)
} finally {
setLoading(false)
}
}
useEffect(() => {
handleApiCall()
}, [])
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just out of curiosity, why not handle this in one of the server components? That way you only have to transmit the HTML of the page and you don't have to worry about loading state or whatever.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you mean pulling this out into its own server component? So "OpportunitiesSection" could be the html and such and Opportunities.tsx could be the server component?

I personally find it frustrating when things get too abstracted and there are 6 files that I need to look into to understand what specifically I need to edit/change; projects get overwhelming as the components folder grows; however, this is running on each page load which is probably overkill?

Thinking about it, this is worth moving for functionality; I need some help thinking about file structure in the future though!

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sortof! This is informed by a convention I've come to for myself. When possible, I like to do all my fetching / data interactions at the highest component level I can / is reasonable. For NextJS, it's on the page.tsx level. And I think some developers on Next JS and I are of the same opinion about this because all page.tsx components are async by default!

So the pattern would be that you do your asynchronous fetch action in the page, await the result, and then pass the resolved data to the UI component later on. For this example, it might look something like this:

app/about/page.tsx

export default async function About() {
  try {
    // Omitting function definition for brevity
    const data: OpportunityProps = await handleApiCall();
    return <Opportunities data={data} />;
  }
  catch (ex) {
    // Handle the case when the fetch fails for whatever reason, display a different page.
    return <p>{ex}</p>;
  }
}

components/Opportunities.tsx

"use client";
export function Opportunities({ data }: {data:OpportunityProps}) {
  // However you want to display the fetched data, you can omit the fetch effect because you're getting passed validated data.
  return (
    <>
      {data.map( /* ... */)}
    </>
  );
}

Does this make sense? I got a little lazy when writing this 😓


return (
<section>
<Link href="#opportunities" className="text-neutral-900">
<h2 className="text-3xl bg-primary-500 text-white p-4 flex items-center gap-2">
<UserPlusIcon className="h-7 w-7" />
Opportunities
</h2>
</Link>
{isLoading && <p className="px-2 font-bold">Loading...</p>}
hetd54 marked this conversation as resolved.
Show resolved Hide resolved
{error && <p className="px-2 font-bold text-red">Error: {error}</p>}
{data.jobPostings.length > 0 &&
data.jobPostings.map((position) => {
return (
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a very small thing, but when I don't have any Logic happening inside an anonymous function like this, I tend to like to omit the return statement. Now sure if that's your vibe too, but I think the code ends up looking cleaner.

<div
key={position.externalPath}
className="flex flex-col m-4 gap-6"
>
<a
key={position.externalPath}
className="position-block text-secondary-blue-700 hover:text-black"
href={`https://brown.wd5.myworkdayjobs.com/en-US/staff-careers-brown${position.externalPath}`}
>
<LinkCard>
<div className="flex flex-col gap-6 md:items-center md:justify-between md:flex-row">
<div className="flex flex-col gap-2">
<p className="flex items-center text-secondary-blue-700 gap-2 md:gap-4">
<MapPinIcon className="h-4 w-4" /> Providence, RI -
United States
</p>
<p className="text-gray-800">{position.title}</p>
Comment on lines +39 to +43
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wonder if things like this should be p's or span's... MDN didn't come down one way or the other, but I think there might be a reason to use something more generic here, in that this doesn't represent a paragraph in totality? Not sure though!!!

</div>

<div className="flex items-center gap-2 md:gap-6">
<p>Learn More</p>
<ArrowRightIcon className="h-4 w-4" />
</div>
</div>
</LinkCard>
</a>
</div>
)
})}
</section>
)
}
Loading