diff --git a/src/landings/components/journey/JourneyLanding.tsx b/src/landings/components/journey/JourneyLanding.tsx index 7eb489fe0e..2dc85368e7 100644 --- a/src/landings/components/journey/JourneyLanding.tsx +++ b/src/landings/components/journey/JourneyLanding.tsx @@ -1,16 +1,105 @@ import { DefaultLayout } from '@/frame/components/DefaultLayout' import { useLandingContext } from '@/landings/context/LandingContext' import { LandingHero } from '@/landings/components/shared/LandingHero' +import { JourneyLearningTracks } from './JourneyLearningTracks' + +export type JourneyLearningTrack = { + id: string + title: string + description: string + trackName: string + trackProduct: string + guides?: Array<{ + href: string + title: string + }> +} export const JourneyLanding = () => { const { title, intro, heroImage, introLinks } = useLandingContext() + // Temp until we hookup real data + const stubLearningTracks: JourneyLearningTrack[] = [ + { + id: 'admin:get_started_with_your_enterprise_account', + title: 'Get started with your enterprise account', + description: + 'Set up your enterprise account and configure initial settings for your organization.', + trackName: 'get_started_with_your_enterprise_account', + trackProduct: 'admin', + guides: [ + { + href: '/admin/overview/about-enterprise-accounts?learn=get_started_with_your_enterprise_account&learnProduct=admin', + title: 'About enterprise accounts', + }, + { + href: '/admin/managing-accounts-and-repositories/managing-users-in-your-enterprise/inviting-people-to-manage-your-enterprise?learn=get_started_with_your_enterprise_account&learnProduct=admin', + title: 'Inviting people to manage your enterprise', + }, + { + href: '/admin/policies/enforcing-policies-for-your-enterprise/about-enterprise-policies?learn=get_started_with_your_enterprise_account&learnProduct=admin', + title: 'About enterprise policies', + }, + ], + }, + { + id: 'admin:adopting_github_actions_for_your_enterprise_ghec', + title: 'Adopt GitHub Actions for your enterprise', + description: + 'Learn how to plan and implement a rollout of GitHub Actions in your enterprise.', + trackName: 'adopting_github_actions_for_your_enterprise_ghec', + trackProduct: 'admin', + guides: [ + { + href: '/admin/managing-github-actions-for-your-enterprise/getting-started-with-github-actions-for-your-enterprise/about-github-actions-for-enterprises?learn=adopting_github_actions_for_your_enterprise_ghec&learnProduct=admin', + title: 'About GitHub Actions for enterprises', + }, + { + href: '/actions/get-started/understand-github-actions?learn=adopting_github_actions_for_your_enterprise_ghec&learnProduct=admin', + title: 'Understanding GitHub Actions', + }, + { + href: '/admin/managing-github-actions-for-your-enterprise/getting-started-with-github-actions-for-your-enterprise/introducing-github-actions-to-your-enterprise?learn=adopting_github_actions_for_your_enterprise_ghec&learnProduct=admin', + title: 'Introducing GitHub Actions to your enterprise', + }, + { + href: '/admin/managing-github-actions-for-your-enterprise/getting-started-with-github-actions-for-your-enterprise/migrating-your-enterprise-to-github-actions?learn=adopting_github_actions_for_your_enterprise_ghec&learnProduct=admin', + title: 'Migrating your enterprise to GitHub Actions', + }, + ], + }, + { + id: 'actions:continuous-integration', + title: 'Continuous integration with GitHub Actions', + description: + 'Set up automated testing and building for your projects using GitHub Actions workflows.', + trackName: 'continuous-integration', + trackProduct: 'actions', + guides: [ + { + href: '/actions/automating-builds-and-tests/about-continuous-integration?learn=continuous-integration&learnProduct=actions', + title: 'About continuous integration', + }, + { + href: '/actions/automating-builds-and-tests/building-and-testing-nodejs?learn=continuous-integration&learnProduct=actions', + title: 'Building and testing Node.js', + }, + { + href: '/actions/automating-builds-and-tests/building-and-testing-python?learn=continuous-integration&learnProduct=actions', + title: 'Building and testing Python', + }, + ], + }, + ] + return (
-
TODO
+
+ +
) diff --git a/src/landings/components/journey/JourneyLearningTracks.module.css b/src/landings/components/journey/JourneyLearningTracks.module.css new file mode 100644 index 0000000000..b0dc03bfa5 --- /dev/null +++ b/src/landings/components/journey/JourneyLearningTracks.module.css @@ -0,0 +1,213 @@ +.learningTracks { + border: 1px solid var(--borderColor-default, var(--color-border-default, #d1d9e0)); + border-radius: 12px; + padding: 1.5rem; + padding-bottom: .75rem; + margin-bottom: 1rem; + margin-left: 1rem; + box-shadow: + 0px 1px 3px 0px rgba(31, 35, 40, 0.08), + 0px 1px 0px 0px rgba(31, 35, 40, 0.06); + position: relative; + z-index: 1; + background-color: var(--bgColor-default, var(--color-canvas-default, #ffffff)); +} + +.trackHeader { + margin: 0 0 0.5rem 0; + padding-right: 3rem; + color: var(--fgColor-default, var(--color-fg-default, #1f2328)); + display: flex; + align-items: center; + gap: 0.5rem; +} + +.anchorLink { + color: var(--fgColor-default, var(--color-fg-default, #1f2328)); + text-decoration: none; +} + +.trackDescription { + margin: 0 0 1rem 0; + color: var(--fgColor-muted, var(--color-fg-muted, #656d76)); +} + +.expandButton { + top: 0; + right: 0; +} + +.trackGuides { + margin-top: 1rem; + margin-bottom: 1rem; + padding-left: 0; + list-style: none; + counter-reset: guide-counter; +} + +.trackGuides li { + margin-bottom: 0.75rem; + padding-left: 2.5rem; + position: relative; + counter-increment: guide-counter; +} + +.trackGuides li::before { + content: counter(guide-counter); + position: absolute; + left: 0; + top: -0.125rem; + width: 1.5rem; + height: 1.5rem; + background-color: transparent; + border: 1px solid var(--borderColor-default, var(--color-border-default, #d0d7de)); + color: var(--fgColor-muted, var(--color-fg-muted, #656d76)); + border-radius: 50%; + display: flex; + align-items: center; + justify-content: center; + font-size: 0.70rem; + font-weight: 600; + line-height: 1; +} + +.guideLink { + color: var(--fgColor-accent, var(--color-accent-fg, #0969da)); + text-decoration: none; +} + +/* Hide only the timeline line that extends below the last badge, preserve everything else */ +.timelineContainer :global(.Timeline-Item:last-child::before) { + background: linear-gradient(to bottom, var(--borderColor-default, #d1d9e0) 0%, var(--borderColor-default, #d1d9e0) 30%, transparent 30%, transparent 100%) !important; +} + +.timelineBadge { + background-color: var(--color-canvas-subtle, #f6f8fa) !important; + border: 1px solid var(--borderColor-default, var(--color-border-default, #d1d9e0)) !important; +} + +/* Fix entire timeline component overlapping header */ +.timelineContainer { + z-index: 0 !important; + position: relative; +} + +.timelineContainer :global(.Timeline) { + z-index: 0 !important; +} + +.timelineThinLine :global(.Timeline-Item::before) { + width: 1px !important; +} + +/* Mobile-first: custom stacked layout */ +.mobileLayout { + display: block; + position: relative; + z-index: 0; +} + +.mobileItem { + margin-bottom: 2rem; + text-align: center; /* Only for centering the badge */ + position: relative; +} + +.mobileBadge { + width: 2rem; + height: 2rem; + background-color: var(--color-canvas-subtle, #f6f8fa); + color: var(--fgColor-muted, var(--color-fg-muted)); + border: 1px solid var(--borderColor-default, var(--color-border-default, #d1d9e0)); + border-radius: 50%; + display: inline-flex; + align-items: center; + justify-content: center; + font-weight: 600; + margin-bottom: 0.5rem; + position: relative; + z-index: 2; +} + +/* Add connecting line from badge downward */ +.mobileBadge::after { + content: ''; + position: absolute; + left: 50%; + top: 100%; + width: 1px; + height: 2.5rem; + background-color: var(--borderColor-default, var(--color-border-default, #d1d9e0)); + transform: translateX(-50%); + z-index: 1; +} + +/* Add connecting line above badge (except first item) */ +.mobileItem:not(:first-child) .mobileBadge::before { + content: ''; + position: absolute; + left: 50%; + bottom: 100%; + width: 1px; + height: 2.5rem; + background-color: var(--borderColor-default, var(--color-border-default, #d1d9e0)); + transform: translateX(-50%); + z-index: 1; +} + +.mobileConnector { + width: 1px; + height: 1rem; + background-color: var(--borderColor-default, var(--color-border-default, #d1d9e0)); + margin: 0 auto; +} + +.mobileConnector:last-child { + display: none; /* Hide connector after last item */ +} + +.mobileItem .mobileTile { + border: 1px solid var(--borderColor-default, var(--color-border-default, #d1d9e0)); + border-radius: 12px; + padding: 1rem; + margin-top: 0.5rem; + text-align: left; + box-shadow: + 0px 1px 3px 0px rgba(31, 35, 40, 0.08), + 0px 1px 0px 0px rgba(31, 35, 40, 0.06); + position: relative; + z-index: 3; + background-color: var(--bgColor-default, var(--color-canvas-default, #ffffff)); +} + +/* Desktop: show Timeline component */ +@media (min-width: 768px) { + .mobileLayout { + display: none; + } + + .timelineContainer { + display: block; + } +} + +/* Mobile: hide Timeline component */ +@media (max-width: 767px) { + .timelineContainer { + display: none; + } +} + +/* Mobile: stack h3 and Token vertically when mobile layout is active */ +@media (max-width: 767px) { + .trackHeader { + flex-direction: column; + align-items: flex-start; + gap: 0.5rem; + margin-bottom: 1.5rem; + } + + .trackHeader h3 { + margin-bottom: .50rem; + } +} \ No newline at end of file diff --git a/src/landings/components/journey/JourneyLearningTracks.tsx b/src/landings/components/journey/JourneyLearningTracks.tsx new file mode 100644 index 0000000000..87397ec49d --- /dev/null +++ b/src/landings/components/journey/JourneyLearningTracks.tsx @@ -0,0 +1,85 @@ +/* filepath: /workspaces/docs-internal/src/landings/components/journey/JourneyLearningTracks.tsx */ +import { ChevronDownIcon, ChevronUpIcon } from '@primer/octicons-react' +import { Button, Details, Timeline, Token, useDetails } from '@primer/react' +import type { JourneyLearningTrack } from './JourneyLanding' +import styles from './JourneyLearningTracks.module.css' + +type JourneyLearningTracksProps = { + tracks: JourneyLearningTrack[] +} + +export const JourneyLearningTracks = ({ tracks }: JourneyLearningTracksProps) => { + if (!tracks || tracks.length === 0) { + return null + } + + const renderTrackContent = (track: JourneyLearningTrack, trackIndex: number) => { + const { getDetailsProps, open } = useDetails({}) + + return ( + <> +
+

{track.title}

+ +
+

{track.description}

+
+ +
    + {(track.guides || []).map((guide) => ( +
  1. + + {guide.title} + +
  2. + ))} +
+
+ + ) + } + + return ( + <> + {/* Desktop: Timeline component */} +
+ + {tracks.map((track, trackIndex) => { + return ( + + {trackIndex + 1} + +
{renderTrackContent(track, trackIndex)}
+
+
+ ) + })} +
+
+ + {/* Mobile: Custom stacked layout */} +
+ {tracks.map((track, trackIndex) => ( +
+
{trackIndex + 1}
+
+
{renderTrackContent(track, trackIndex)}
+
+ {trackIndex < tracks.length - 1 &&
} +
+ ))} +
+ + ) +}