Repository navigation
feat(nuxters): move nuxters.nuxt.com to nuxt.com/nuxters #2458
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
Merged
Merged
Changes from all commits
Commits
Show all changes
34 commits
Select commit
Hold shift + click to select a range
bcd504f
refactor(app): use getter keys for navigation and content heads data
atinux b5e516b
feat(nuxters): move nuxters.nuxt.com to nuxt.com/nuxters
atinux 1b02817
feat(nuxters): put the profile next to its stats, one GitHub icon
atinux e1a63aa
feat(nuxters): color the profile stats like the README card
atinux 0ee587a
feat(nuxters): profile periods, Nuxter search and header navigation
atinux 81ab70b
fix(nuxters): address review feedback
atinux d266460
up
atinux 51df33f
chore: add admin sync
atinux a1c985f
feat(nuxters): share the period in titles, descriptions and cards
atinux 4403182
more improvements
atinux 2ca9124
Merge branch 'main' into feat/nuxters
atinux 820c1e3
chore: refacto to use NuxtersPeriodTab component
atinux 0c26c2d
Update [username].vue
atinux f68ba48
feat(nuxters): show the GitHub profile links of Nuxters
atinux 0c82229
feat(nuxters): show GitHub with the other profile links
atinux 4cf3bfb
feat(nuxters): share actions for the profile owner and in the Nuxter …
atinux 9a3a04a
Merge remote-tracking branch 'origin/main' into feat/nuxters
atinux 4de9dd4
feat(nuxters): maintained and contributed modules on profiles
atinux c71e0a2
feat(nuxi): Nuxter card in the chat, modules in get-nuxter
atinux c978a0e
feat(blog): GitHub usernames for authors, blog posts on Nuxter profiles
atinux 2b16bf0
feat(modules): link maintainers and contributors to their Nuxter profile
atinux fdf877e
feat(nuxters): period tabs with UTabs, no scroll on period change
benjamincanac a2dbd61
fix(nuxters): keep #leaderboard in view once the hero card loads
benjamincanac 80a9210
feat(nuxters): tinted stat cards, compact blog posts and show all on …
benjamincanac 1074082
feat(nuxi): Nuxter card links as a whole, stats without labels
benjamincanac cf5706a
fix(nuxters): sharp leaderboard avatars
atinux e66e17e
fix(nuxters): no layout shift from the owner share block on profiles
benjamincanac e5663c9
feat(nuxters): 4 column grids with aligned gaps on profiles
benjamincanac fa8903b
feat(nuxters): people-first hero
atinux ba5d1af
fix(nuxters): harden module maintainers, profile links and profiles i…
atinux 20c18ac
fix(nuxters): no layout shift in the hero, period picker for the READ…
atinux 13e9a0f
chore: remove extra hash
atinux f46ae36
fix(nuxters): read the Discord env vars of the Vercel project
atinux 432ce99
feat(nuxters): centered sign-in card, hero thanks the contributors
atinux File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -28,6 +28,7 @@ test-results/ | |
| .env*.local | ||
| .env* | ||
| !.env.example | ||
| /.swc | ||
|
|
||
| # Amp orb portal manifests | ||
| .amp/portals/* | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,96 @@ | ||
| <script lang="ts" setup> | ||
| import type { NuxterProfile } from '#shared/types' | ||
| import { isNuxtersPeriod, nuxterForPeriod, nuxtersPeriodPhrase } from '#shared/utils/nuxters' | ||
|
|
||
| // Only the username and the period are in the image URL: the stats are read at render time, | ||
| // so a README card stays the same URL and shows fresh numbers. | ||
| const props = defineProps<{ | ||
| username: string | ||
| /** `all` (default), `30d`, `12m` or a year: the stats of a shared `?period=` link. */ | ||
| period?: string | ||
| }>() | ||
|
|
||
| const profile = await $fetch<NuxterProfile>(`/api/nuxters/${props.username}`) | ||
| // Not named `period`: a local variable with a prop's name hides the prop from nuxt-og-image, | ||
| // which then drops it from the image URL (it only keeps the declared props). | ||
| // The image URL decodes `period_2024` as the number 2024: compare it as a string. | ||
| const raw = props.period == null ? undefined : String(props.period) | ||
| const requested = isNuxtersPeriod(raw) ? raw : 'all' | ||
| // A period without contributions shows all time, like the profile page. | ||
| const nuxter = nuxterForPeriod(profile, requested) ?? profile | ||
| const shown = nuxter === profile ? 'all' : requested | ||
| const since = profile.firstContributionAt ? new Date(profile.firstContributionAt).getUTCFullYear() : null | ||
|
|
||
| const { format } = new Intl.NumberFormat('en-US') | ||
|
|
||
| const label = (value: number, one: string, many: string) => value === 1 ? one : many | ||
|
|
||
| // Two explicit rows: takumi does not wrap flex items. | ||
| const rows = [ | ||
| [ | ||
| { label: label(nuxter.mergedPullRequests.all, 'Merged PR', 'Merged PRs'), value: nuxter.mergedPullRequests.all, color: '#00DC82' }, | ||
| { label: label(nuxter.issues, 'Issue', 'Issues'), value: nuxter.issues, color: '#38BDF8' } | ||
| ], | ||
| [ | ||
| { label: label(nuxter.comments, 'Comment', 'Comments'), value: nuxter.comments, color: '#A78BFA' }, | ||
| { label: label(nuxter.reactions, 'Reaction', 'Reactions'), value: nuxter.reactions, color: '#FACC15' } | ||
| ] | ||
| ] | ||
| </script> | ||
|
|
||
| <template> | ||
| <div class="size-full flex flex-row bg-neutral-950 text-white p-16 gap-16"> | ||
| <div class="flex flex-col w-[380px] items-center justify-between"> | ||
| <div class="flex flex-col items-center"> | ||
| <img | ||
| :src="`https://avatars.githubusercontent.com/u/${nuxter.githubId}?s=256`" | ||
| class="size-[176px] rounded-full" | ||
| :style="{ border: '4px solid #262626' }" | ||
| width="176" | ||
| height="176" | ||
| > | ||
| <p class="text-5xl font-semibold mt-6"> | ||
| {{ nuxter.username }} | ||
| </p> | ||
| <p class="text-3xl text-neutral-400 mt-4"> | ||
| {{ shown === 'all' ? `Nuxter #${format(nuxter.rank)}` : `#${format(nuxter.rank)} ${nuxtersPeriodPhrase(shown)}` }} | ||
| </p> | ||
| <p class="text-4xl font-semibold text-green-400 mt-4"> | ||
| {{ format(nuxter.score) }} {{ nuxter.score === 1 ? 'pt' : 'pts' }} | ||
| </p> | ||
| <p v-if="since" class="text-2xl text-neutral-500 mt-3"> | ||
| Nuxter since {{ since }} | ||
| </p> | ||
| </div> | ||
|
|
||
| <svg | ||
| viewBox="0 0 256 168" | ||
| xmlns="http://www.w3.org/2000/svg" | ||
| width="64" | ||
| height="42" | ||
| class="w-[64px] h-[42px] shrink-0" | ||
| preserveAspectRatio="xMidYMid meet" | ||
| > | ||
| <path fill="#00DC82" d="M143.618 167.029h95.166c3.023 0 5.992-.771 8.61-2.237a16.963 16.963 0 0 0 6.302-6.115 16.324 16.324 0 0 0 2.304-8.352c0-2.932-.799-5.811-2.312-8.35L189.778 34.6a16.966 16.966 0 0 0-6.301-6.113 17.626 17.626 0 0 0-8.608-2.238c-3.023 0-5.991.772-8.609 2.238a16.964 16.964 0 0 0-6.3 6.113l-16.342 27.473-31.95-53.724a16.973 16.973 0 0 0-6.304-6.112A17.638 17.638 0 0 0 96.754 0c-3.022 0-5.992.772-8.61 2.237a16.973 16.973 0 0 0-6.303 6.112L2.31 141.975A16.302 16.302 0 0 0 0 150.325c0 2.932.793 5.813 2.304 8.352a16.964 16.964 0 0 0 6.302 6.115 17.628 17.628 0 0 0 8.61 2.237h59.737c23.669 0 41.123-10.084 53.134-29.758l29.159-48.983 15.618-26.215 46.874 78.742h-62.492l-15.628 26.214Zm-67.64-26.24-41.688-.01L96.782 35.796l31.181 52.492-20.877 35.084c-7.976 12.765-17.037 17.416-31.107 17.416Z" /> | ||
| </svg> | ||
| </div> | ||
|
|
||
| <div class="flex flex-col flex-1 gap-8 justify-center"> | ||
| <div v-for="(row, index) in rows" :key="index" class="flex flex-row gap-8"> | ||
| <div | ||
| v-for="stat in row" | ||
| :key="stat.label" | ||
| class="flex flex-col justify-end flex-1 h-[220px] rounded-3xl bg-neutral-900 p-8" | ||
| :style="{ border: `2px solid ${stat.color}` }" | ||
| > | ||
| <p class="text-6xl font-semibold"> | ||
| {{ format(stat.value) }} | ||
| </p> | ||
| <p class="text-3xl mt-2" :style="{ color: stat.color }"> | ||
| {{ stat.label }} | ||
| </p> | ||
| </div> | ||
| </div> | ||
| </div> | ||
| </div> | ||
| </template> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,80 @@ | ||
| <script setup lang="ts"> | ||
| import type { NuxtersPage, NuxterSummary } from '#shared/types' | ||
|
|
||
| const props = defineProps<{ | ||
| initial: NuxtersPage | ||
| }>() | ||
|
|
||
| const PAGE_SIZE = 100 | ||
|
|
||
| const { format } = new Intl.NumberFormat('en-US') | ||
| const points = (score: number) => `${format(score)} ${score === 1 ? 'pt' : 'pts'}` | ||
|
|
||
| const more = ref<NuxterSummary[]>([]) | ||
| const loading = ref(false) | ||
| const items = computed(() => [...props.initial.items, ...more.value]) | ||
| const total = computed(() => props.initial.total) | ||
|
|
||
| // The profile opens on the same period (all time is its default). | ||
| const profileQuery = computed(() => props.initial.period === 'all' ? {} : { period: props.initial.period }) | ||
|
|
||
| // Another period: start again from its first page. | ||
| watch(() => props.initial, () => { | ||
| more.value = [] | ||
| }) | ||
|
|
||
| async function showMore() { | ||
| // The period can change while the page loads: its items must not join the new list. | ||
| const initial = props.initial | ||
| loading.value = true | ||
| try { | ||
| const page = await $fetch<NuxtersPage>('/api/nuxters', { query: { period: initial.period, offset: items.value.length, limit: PAGE_SIZE } }) | ||
| if (initial === props.initial) more.value.push(...page.items) | ||
| } finally { | ||
| loading.value = false | ||
| } | ||
| } | ||
| </script> | ||
|
|
||
| <template> | ||
| <div> | ||
| <ul class="grid grid-cols-5 sm:grid-cols-8 md:grid-cols-10 gap-3 sm:gap-4"> | ||
| <li v-for="nuxter in items" :key="nuxter.githubId" class="relative"> | ||
| <UTooltip :text="`${nuxter.username} · ${points(nuxter.score)}`"> | ||
| <NuxtLink | ||
| :to="{ path: `/nuxters/${nuxter.username}`, query: profileQuery }" | ||
| :aria-label="`${nuxter.username}, #${format(nuxter.rank)}, ${points(nuxter.score)}`" | ||
| class="block aspect-square rounded-lg overflow-hidden bg-muted ring ring-default hover:ring-primary transition" | ||
| > | ||
| <!-- Cells are 123px at most (10 columns of the full container): 124px, and 248px on 2x screens --> | ||
| <NuxtImg | ||
| :src="`/gh_avatar/${nuxter.username}`" | ||
| provider="ipx" | ||
| densities="x1 x2" | ||
| width="124" | ||
| height="124" | ||
| format="auto" | ||
| loading="lazy" | ||
| :alt="nuxter.username" | ||
| class="size-full" | ||
| /> | ||
| </NuxtLink> | ||
| </UTooltip> | ||
| <span class="absolute -bottom-1.5 right-1 px-1 rounded bg-default ring ring-default text-xs font-medium tabular-nums"> | ||
| {{ nuxter.rank }} | ||
| </span> | ||
| </li> | ||
| </ul> | ||
|
|
||
| <div v-if="items.length < total" class="flex justify-center mt-10"> | ||
| <UButton | ||
| label="Show more" | ||
| icon="i-lucide-plus" | ||
| color="neutral" | ||
| variant="outline" | ||
| :loading="loading" | ||
| @click="showMore" | ||
| /> | ||
| </div> | ||
| </div> | ||
| </template> | ||
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.