@@ -62,9 +67,17 @@ const ariaLabel = computed(() => t('team.card_aria', { name: props.name, role: p
/>
+
+
+
“{{ slogan }}”
{{ t('team.view_profile') }}
diff --git a/components/features/home/team/TeamMembers.vue b/components/features/home/team/TeamMembers.vue
index a687c3a..8b28102 100644
--- a/components/features/home/team/TeamMembers.vue
+++ b/components/features/home/team/TeamMembers.vue
@@ -2,6 +2,7 @@
import TeamMemberCard from './TeamMemberCard.vue'
import { useI18n } from 'vue-i18n'
import type { TeamMember } from '~/types/team'
+import { toRoleList } from '~/utils/teamRoles'
type Props = {
title?: string
@@ -23,7 +24,10 @@ const selectedRole = ref('')
const visibleCount = ref(props.limit)
const roles = computed(() => {
- const set = new Set(props.members.map(m => m.role).filter(Boolean))
+ const set = new Set()
+ for (const m of props.members) {
+ for (const r of toRoleList(m.role)) set.add(r)
+ }
return Array.from(set)
})
@@ -31,7 +35,7 @@ const filtered = computed(() => {
const q = query.value.trim().toLowerCase()
const r = selectedRole.value
let list = props.members
- if (r) list = list.filter(m => m.role === r)
+ if (r) list = list.filter(m => toRoleList(m.role).includes(r))
if (q) list = list.filter(m => m.name.toLowerCase().includes(q))
return list
})
diff --git a/components/features/team/OpenPositionCard.vue b/components/features/team/OpenPositionCard.vue
index bebcfba..16de458 100644
--- a/components/features/team/OpenPositionCard.vue
+++ b/components/features/team/OpenPositionCard.vue
@@ -2,19 +2,33 @@
import { useI18n } from 'vue-i18n'
import { FontAwesomeIcon } from '@fortawesome/vue-fontawesome'
import { faDiscord } from '@fortawesome/free-brands-svg-icons'
+import { faHandHoldingHeart } from '@fortawesome/free-solid-svg-icons'
+import { toRoleString } from '~/utils/teamRoles'
type Props = {
- role: string
+ role: string | string[]
slogan?: string
applyUrl?: string
+ applyVia?: 'discord' | 'opencollective'
}
const props = withDefaults(defineProps(), {
slogan: undefined,
- applyUrl: 'https://1lf.link/discord'
+ applyUrl: 'https://1lf.link/discord',
+ applyVia: 'discord'
})
const { t } = useI18n()
+
+const roleText = computed(() => toRoleString(props.role))
+const isOpenCollective = computed(() => props.applyVia === 'opencollective')
+const icon = computed(() => isOpenCollective.value ? faHandHoldingHeart : faDiscord)
+const applyLabel = computed(() => isOpenCollective.value
+ ? t('team.open_position.apply_opencollective')
+ : t('team.open_position.apply'))
+const applyAria = computed(() => isOpenCollective.value
+ ? t('team.open_position.apply_aria_opencollective', { role: roleText.value })
+ : t('team.open_position.apply_aria', { role: roleText.value }))
@@ -29,19 +43,19 @@ const { t } = useI18n()
{{ t('team.open_position.badge') }}
- {{ props.role }}
+ {{ roleText }}
- {{ props.slogan }}
+ {{ props.slogan }}
-
- {{ t('team.open_position.apply') }}
+
+ {{ applyLabel }}
diff --git a/components/features/team/TeamFaqSection.vue b/components/features/team/TeamFaqSection.vue
new file mode 100644
index 0000000..4387fc8
--- /dev/null
+++ b/components/features/team/TeamFaqSection.vue
@@ -0,0 +1,85 @@
+
+
+
+
+
+
+ {{ t('team.faq.section_title') }}
+
+
+ {{ t('team.faq.section_subtitle') }}
+
+
+
+
+
+
+ {{ entry.question }}
+ +
+
+
+
+
+
+
+
+
diff --git a/components/features/team/TeamRankSection.vue b/components/features/team/TeamRankSection.vue
index ff4930d..83e9555 100644
--- a/components/features/team/TeamRankSection.vue
+++ b/components/features/team/TeamRankSection.vue
@@ -39,6 +39,7 @@ const profileHref = (slug?: string) => slug ? `/${locale.value}/team/${slug}` :
:role="p.role || rankLabel"
:slogan="p.slogan"
:apply-url="p.applyUrl"
+ :apply-via="p.applyVia"
/>
diff --git a/composables/useTeamFaqContent.ts b/composables/useTeamFaqContent.ts
new file mode 100644
index 0000000..9acfbc0
--- /dev/null
+++ b/composables/useTeamFaqContent.ts
@@ -0,0 +1,24 @@
+import { useContentRepository } from '~/composables/useContentRepository'
+import type { Locale } from '~/utils/content/collections'
+import type { TeamFaqEntry } from '~/types/faq'
+
+/**
+ * Fetches the application/rank-requirement FAQ for the active locale,
+ * ordered by the optional `order` frontmatter field. Kept separate from
+ * the home FAQ so the two surfaces can evolve independently.
+ */
+export function useTeamFaqContent() {
+ const { locale } = useI18n()
+ const repo = useContentRepository()
+ const activeLocale = computed(() => (locale?.value || 'en') as Locale)
+
+ const { data: entries } = useAsyncData(
+ () => `team-faq-${activeLocale.value}`,
+ () => repo.listTeamFaqEntries(activeLocale.value),
+ { watch: [activeLocale] }
+ )
+
+ const items = computed(() => entries.value || [])
+
+ return { items }
+}
diff --git a/composables/useTeamProfile.ts b/composables/useTeamProfile.ts
index efbef9e..b817da2 100644
--- a/composables/useTeamProfile.ts
+++ b/composables/useTeamProfile.ts
@@ -3,7 +3,14 @@ import type { Locale } from '~/utils/content/collections'
import type { TeamDocument, TeamMember } from '~/types/team'
import { teamAvatarUrl } from '~/utils/teamAvatar'
-export function useTeamProfile(slugOverride?: string) {
+/**
+ * Resolves the active team member synchronously on SSR by awaiting the
+ * underlying `useAsyncData` call. This is what lets `usePageSeo` see the
+ * real member name/bio when it runs in the page's setup — without the
+ * await, meta tags ship with the "Team" fallback because the member ref
+ * is still null at SSR render time.
+ */
+export async function useTeamProfile(slugOverride?: string) {
const route = useRoute()
const { locale } = useI18n()
const repo = useContentRepository()
@@ -11,7 +18,7 @@ export function useTeamProfile(slugOverride?: string) {
const slug = computed(() => slugOverride ?? (route.params.slug as string))
- const { data: teamDoc } = useAsyncData(
+ const { data: teamDoc } = await useAsyncData(
() => `team-profile-${activeLocale.value}`,
() => repo.getTeamDocument(activeLocale.value),
{ watch: [activeLocale] }
diff --git a/content.config.ts b/content.config.ts
index 26c9e25..e219d4e 100644
--- a/content.config.ts
+++ b/content.config.ts
@@ -86,9 +86,12 @@ const teamSchema = z
id: z.union([z.string(), z.number()]),
name: z.string(),
slug: z.string().optional(),
- role: z.string().optional(),
+ // Sub-role label(s). A single string keeps existing entries
+ // valid; an array renders one chip per discipline for members
+ // who wear several hats (e.g. development + building).
+ role: z.union([z.string(), z.array(z.string())]).optional(),
// Coarse group for sectioning/ordering; specific job stays in `role`.
- rank: z.enum(['admin', 'content', 'moderation']).optional(),
+ rank: z.enum(['admin', 'teamassist', 'content', 'moderation', 'media', 'lite']).optional(),
slogan: z.string().optional(),
bio: z.string().optional(),
since: z.string().optional(),
@@ -98,7 +101,10 @@ const teamSchema = z
avatarUrl: z.string().url().optional(),
// Marks an entry as an open position rather than a real member.
openPosition: z.boolean().optional(),
- applyUrl: z.string().url().optional()
+ applyUrl: z.string().url().optional(),
+ // Channel for an open position: Discord (regular hiring) or
+ // OpenCollective (the donation-funded Lite rank).
+ applyVia: z.enum(['discord', 'opencollective']).optional()
})
.passthrough()
)
@@ -200,6 +206,11 @@ export default defineContentConfig({
source: `faq/${locale}/*.md`,
schema: faqSchema
})),
+ ...defineLocalizedCollections('team_faq', (locale) => ({
+ type: 'page',
+ source: `team-faq/${locale}/*.md`,
+ schema: faqSchema
+ })),
authors: defineCollection({
type: 'page',
source: 'authors/**/*.md',
diff --git a/content/team-faq/de/apply.md b/content/team-faq/de/apply.md
new file mode 100644
index 0000000..3932859
--- /dev/null
+++ b/content/team-faq/de/apply.md
@@ -0,0 +1,11 @@
+---
+key: apply
+question: Wie kann ich mich auf eine offene Stelle bewerben?
+order: 1
+---
+
+Bewerbungen laufen über unseren Discord. Schreibe dort einer Person aus
+der Administration oder Teamassistenz und nenne kurz, für welche Rolle
+du dich interessierst und was du mitbringst.
+
+Discord beitreten: [1lf.link/discord](https://1lf.link/discord)
diff --git a/content/team-faq/de/lite.md b/content/team-faq/de/lite.md
new file mode 100644
index 0000000..5269bd0
--- /dev/null
+++ b/content/team-faq/de/lite.md
@@ -0,0 +1,14 @@
+---
+key: lite
+question: Was ist der Lite-Rang und wie bekomme ich ihn?
+order: 4
+---
+
+Der Lite-Rang ist kein klassischer Bewerbungsrang. Er wird ausschliesslich
+über freiwillige Spenden auf OpenCollective vergeben und gilt als Dank für
+die finanzielle Unterstützung des Netzwerks.
+
+Die Vergabe ist nicht garantiert: Sie erfolgt auf freiwilliger Basis durch
+die Administration, abhängig vom Spendenstand und der laufenden Finanzierung.
+
+Spenden auf [OpenCollective](https://opencollective.com/onelitefeather).
diff --git a/content/team-faq/de/no-position.md b/content/team-faq/de/no-position.md
new file mode 100644
index 0000000..b6d517b
--- /dev/null
+++ b/content/team-faq/de/no-position.md
@@ -0,0 +1,9 @@
+---
+key: no-position
+question: Ich habe Interesse, sehe aber keine passende Stelle. Was kann ich tun?
+order: 5
+---
+
+Schreibe uns trotzdem auf Discord. Wir nehmen Initiativbewerbungen auf,
+wenn die Person zum Team passt und Bedarf entstehen könnte. Eine Garantie
+auf einen Platz gibt es nicht, aber ehrliches Interesse zählt immer.
diff --git a/content/team-faq/de/process.md b/content/team-faq/de/process.md
new file mode 100644
index 0000000..e00da48
--- /dev/null
+++ b/content/team-faq/de/process.md
@@ -0,0 +1,13 @@
+---
+key: process
+question: Wie läuft der Bewerbungsprozess ab?
+order: 2
+---
+
+1. Kurzes Vorstellungsgespräch über Discord (Text oder Voice).
+2. Je nach Rolle eine Probeaufgabe (z. B. Bau-Probe, Code-Snippet,
+ Moderations-Szenario).
+3. Probezeit im Team mit klaren Erwartungen und regelmässigem Feedback.
+
+Wir antworten in der Regel innerhalb weniger Tage. Wenn es länger dauert,
+darfst du jederzeit freundlich nachhaken.
diff --git a/content/team-faq/de/requirements.md b/content/team-faq/de/requirements.md
new file mode 100644
index 0000000..4048279
--- /dev/null
+++ b/content/team-faq/de/requirements.md
@@ -0,0 +1,25 @@
+---
+key: requirements
+question: Welche Anforderungen gibt es für die einzelnen Ränge?
+order: 3
+---
+
+**Administration:** Mehrjährige Erfahrung im Betrieb von Minecraft-Netzwerken,
+Verantwortung für Infrastruktur, Team und Strategie.
+
+**Teamassistenz:** Unterstützt die Administration im Tagesgeschäft. Gute
+Kommunikation, Zuverlässigkeit und ein Überblick über alle Teilbereiche.
+
+**Content (Entwicklung, Bau-Team, Konzepte):** Konkretes Portfolio im
+gewünschten Bereich. Entwicklung: Java/Kotlin mit Paper/Velocity oder
+Minestom, dazu ein Grundverständnis von Kubernetes. Bau-Team:
+Referenzbauten. Konzepte: ausgearbeitete Spielmodi oder Eventideen.
+
+**Moderation:** Mindestalter 16, ruhiges Auftreten, regelmässige Online-Zeit
+und Bereitschaft, Regeln fair und konsistent durchzusetzen.
+
+**Media:** Gedacht für YouTuber, Streamer und Content Creator rund um
+das Netzwerk. Eigene Reichweite oder erste Inhalte (Videos, Streams,
+Highlights) und Lust, regelmässig zu veröffentlichen.
+
+**Lite:** Kein Bewerbungsrang. Siehe Frage zum Lite-Rang weiter unten.
diff --git a/content/team-faq/en/apply.md b/content/team-faq/en/apply.md
new file mode 100644
index 0000000..c20ca2b
--- /dev/null
+++ b/content/team-faq/en/apply.md
@@ -0,0 +1,11 @@
+---
+key: apply
+question: How do I apply for an open position?
+order: 1
+---
+
+Applications run through our Discord. Reach out to someone from
+Administration or Team Assistance, briefly tell us which role you are
+interested in and what you bring to the table.
+
+Join Discord: [1lf.link/discord](https://1lf.link/discord)
diff --git a/content/team-faq/en/lite.md b/content/team-faq/en/lite.md
new file mode 100644
index 0000000..459879e
--- /dev/null
+++ b/content/team-faq/en/lite.md
@@ -0,0 +1,15 @@
+---
+key: lite
+question: What is the Lite rank and how do I get it?
+order: 4
+---
+
+The Lite rank is not a classic application rank. It is granted only
+through voluntary donations on OpenCollective and serves as a thank-you
+for financially supporting the network.
+
+Awarding it is not guaranteed: it happens at the discretion of the
+Administration, depending on the current donation level and ongoing
+funding.
+
+Donate on [OpenCollective](https://opencollective.com/onelitefeather).
diff --git a/content/team-faq/en/no-position.md b/content/team-faq/en/no-position.md
new file mode 100644
index 0000000..2d36b0c
--- /dev/null
+++ b/content/team-faq/en/no-position.md
@@ -0,0 +1,9 @@
+---
+key: no-position
+question: I am interested but no role fits. What can I do?
+order: 5
+---
+
+Reach out on Discord anyway. We accept speculative applications when the
+person fits the team and a need could come up. There is no guarantee of
+a seat, but honest interest is always welcome.
diff --git a/content/team-faq/en/process.md b/content/team-faq/en/process.md
new file mode 100644
index 0000000..536b202
--- /dev/null
+++ b/content/team-faq/en/process.md
@@ -0,0 +1,14 @@
+---
+key: process
+question: What does the application process look like?
+order: 2
+---
+
+1. A short introduction chat on Discord (text or voice).
+2. A role-specific trial task where it fits (build sample, code snippet,
+ moderation scenario).
+3. A probation period inside the team with clear expectations and
+ regular feedback.
+
+We usually reply within a few days. If it takes longer, feel free to
+politely follow up.
diff --git a/content/team-faq/en/requirements.md b/content/team-faq/en/requirements.md
new file mode 100644
index 0000000..f1939b8
--- /dev/null
+++ b/content/team-faq/en/requirements.md
@@ -0,0 +1,25 @@
+---
+key: requirements
+question: What are the requirements for each rank?
+order: 3
+---
+
+**Administration:** Multiple years of experience running Minecraft
+networks, responsibility for infrastructure, team and strategy.
+
+**Team Assistance:** Supports Administration in day-to-day operations.
+Strong communication, reliability and an overview across all areas.
+
+**Content (Development, Building Team, Concepts):** A concrete portfolio
+in your area. Development: Java/Kotlin with Paper/Velocity or Minestom,
+plus a working understanding of Kubernetes. Building: reference builds.
+Concepts: fleshed-out game modes or event ideas.
+
+**Moderation:** Minimum age 16, calm presence, regular online time and
+the willingness to enforce rules fairly and consistently.
+
+**Media:** Intended for YouTubers, streamers and content creators
+around the network. Existing reach or first content (videos, streams,
+highlights) and the motivation to publish regularly.
+
+**Lite:** Not an application rank. See the Lite question below.
diff --git a/content/team/de/home.json b/content/team/de/home.json
index e1c5e85..2fff6d2 100644
--- a/content/team/de/home.json
+++ b/content/team/de/home.json
@@ -7,6 +7,7 @@
"slug": "themeinerlp",
"rank": "admin",
"slogan": "Just Sudo IT.",
+ "bio": "Kümmert sich um Kubernetes, Proxmox, Infrastruktur, Entwicklung, DevOps, Strategie und Technical Interviews.",
"href": "/de/team/themeinerlp"
},
{
@@ -15,12 +16,30 @@
"slug": "selenretterin",
"rank": "admin",
"slogan": "aka OneLiteFeather",
+ "bio": "Verantwortlich für Strategie, Marketing und Social Media.",
"href": "/de/team/selenretterin"
},
+ {
+ "id": "theevilreaper",
+ "name": "theEvilReaper",
+ "slug": "theevilreaper",
+ "rank": "admin",
+ "bio": "Macht GitHub, Minestom-Entwicklung, alles rund um Minigames, exotische Entwicklung und Technical Interviews.",
+ "href": "/de/team/theevilreaper"
+ },
+ {
+ "id": "alex-m",
+ "name": "Alex M.",
+ "slug": "alex-m",
+ "role": "Teamassistenz",
+ "rank": "teamassist",
+ "href": "/de/team/alex-m"
+ },
{
"id": "joltra",
"name": "Joltra",
"slug": "joltra",
+ "role": "Entwicklung",
"rank": "content",
"href": "/de/team/joltra"
},
@@ -28,66 +47,51 @@
"id": "theshadowsdust",
"name": "theShadowsDust",
"slug": "theshadowsdust",
+ "role": "Entwicklung",
"rank": "content",
"slogan": "I feel like I'm upside down.",
"href": "/de/team/theshadowsdust"
},
- {
- "id": "alex-m",
- "name": "Alex M.",
- "slug": "alex-m",
- "rank": "content",
- "href": "/de/team/alex-m"
- },
- {
- "id": "bavariankingdom",
- "name": "BavarianKingdom",
- "slug": "bavariankingdom",
- "rank": "content",
- "href": "/de/team/bavariankingdom"
- },
{
"id": "mcmdev",
"name": "mcmdev",
"slug": "mcmdev",
+ "role": "Entwicklung",
"rank": "content",
"href": "/de/team/mcmdev"
},
- {
- "id": "pega",
- "name": "Pega",
- "slug": "pega",
- "rank": "content",
- "href": "/de/team/pega"
- },
{
"id": "random",
"name": "Random",
"slug": "random",
+ "role": "Entwicklung",
"rank": "content",
"href": "/de/team/random"
},
{
- "id": "saynax-jonas",
- "name": "Saynax | Jonas",
- "slug": "saynax-jonas",
+ "id": "pega",
+ "name": "Pega",
+ "slug": "pega",
+ "role": "Bau-Team",
"rank": "content",
- "href": "/de/team/saynax-jonas"
- },
- {
- "id": "theevilreaper",
- "name": "theEvilReaper",
- "slug": "theevilreaper",
- "rank": "admin",
- "href": "/de/team/theevilreaper"
+ "href": "/de/team/pega"
},
{
"id": "weltspielt",
"name": "weltspielt",
"slug": "weltspielt",
+ "role": "Content & Konzepte",
"rank": "content",
"href": "/de/team/weltspielt"
},
+ {
+ "id": "saynax-jonas",
+ "name": "Saynax | Jonas",
+ "slug": "saynax-jonas",
+ "rank": "content",
+ "bio": "Kümmert sich um Infra, DevOps und Security.",
+ "href": "/de/team/saynax-jonas"
+ },
{
"id": "open-content",
"name": "Dein Platz?",
@@ -97,6 +101,15 @@
"openPosition": true,
"applyUrl": "https://1lf.link/discord"
},
+ {
+ "id": "bavariankingdom",
+ "name": "BavarianKingdom",
+ "slug": "bavariankingdom",
+ "role": "Moderation",
+ "rank": "moderation",
+ "mcName": "morelia0815",
+ "href": "/de/team/bavariankingdom"
+ },
{
"id": "b3nny",
"name": "B3nNy",
@@ -113,6 +126,25 @@
"slogan": "Sorge für eine faire und freundliche Community.",
"openPosition": true,
"applyUrl": "https://1lf.link/discord"
+ },
+ {
+ "id": "open-media",
+ "name": "Dein Platz?",
+ "role": "Media",
+ "rank": "media",
+ "slogan": "Für YouTuber, Streamer und alle, die Content rund um den Server machen.",
+ "openPosition": true,
+ "applyUrl": "https://1lf.link/discord"
+ },
+ {
+ "id": "open-lite",
+ "name": "Lite-Rang",
+ "role": "Lite",
+ "rank": "lite",
+ "slogan": "Den Lite-Rang gibt es nur über freiwillige Spenden via OpenCollective. Die Vergabe ist nicht garantiert.",
+ "openPosition": true,
+ "applyUrl": "https://opencollective.com/onelitefeather",
+ "applyVia": "opencollective"
}
]
}
diff --git a/content/team/en/home.json b/content/team/en/home.json
index 7cc9b5a..0826de5 100644
--- a/content/team/en/home.json
+++ b/content/team/en/home.json
@@ -7,6 +7,7 @@
"slug": "themeinerlp",
"rank": "admin",
"slogan": "Just Sudo IT.",
+ "bio": "Handles Kubernetes, Proxmox, infrastructure, development, DevOps, strategy and technical interviews.",
"href": "/en/team/themeinerlp"
},
{
@@ -15,12 +16,30 @@
"slug": "selenretterin",
"rank": "admin",
"slogan": "aka OneLiteFeather",
+ "bio": "Owns strategy, marketing and social media.",
"href": "/en/team/selenretterin"
},
+ {
+ "id": "theevilreaper",
+ "name": "theEvilReaper",
+ "slug": "theevilreaper",
+ "rank": "admin",
+ "bio": "Works on GitHub, Minestom development, everything around minigames, exotic development and technical interviews.",
+ "href": "/en/team/theevilreaper"
+ },
+ {
+ "id": "alex-m",
+ "name": "Alex M.",
+ "slug": "alex-m",
+ "role": "Team Assistance",
+ "rank": "teamassist",
+ "href": "/en/team/alex-m"
+ },
{
"id": "joltra",
"name": "Joltra",
"slug": "joltra",
+ "role": "Development",
"rank": "content",
"href": "/en/team/joltra"
},
@@ -28,66 +47,51 @@
"id": "theshadowsdust",
"name": "theShadowsDust",
"slug": "theshadowsdust",
+ "role": "Development",
"rank": "content",
"slogan": "I feel like I'm upside down.",
"href": "/en/team/theshadowsdust"
},
- {
- "id": "alex-m",
- "name": "Alex M.",
- "slug": "alex-m",
- "rank": "content",
- "href": "/en/team/alex-m"
- },
- {
- "id": "bavariankingdom",
- "name": "BavarianKingdom",
- "slug": "bavariankingdom",
- "rank": "content",
- "href": "/en/team/bavariankingdom"
- },
{
"id": "mcmdev",
"name": "mcmdev",
"slug": "mcmdev",
+ "role": "Development",
"rank": "content",
"href": "/en/team/mcmdev"
},
- {
- "id": "pega",
- "name": "Pega",
- "slug": "pega",
- "rank": "content",
- "href": "/en/team/pega"
- },
{
"id": "random",
"name": "Random",
"slug": "random",
+ "role": "Development",
"rank": "content",
"href": "/en/team/random"
},
{
- "id": "saynax-jonas",
- "name": "Saynax | Jonas",
- "slug": "saynax-jonas",
+ "id": "pega",
+ "name": "Pega",
+ "slug": "pega",
+ "role": "Building Team",
"rank": "content",
- "href": "/en/team/saynax-jonas"
- },
- {
- "id": "theevilreaper",
- "name": "theEvilReaper",
- "slug": "theevilreaper",
- "rank": "admin",
- "href": "/en/team/theevilreaper"
+ "href": "/en/team/pega"
},
{
"id": "weltspielt",
"name": "weltspielt",
"slug": "weltspielt",
+ "role": "Content & Concepts",
"rank": "content",
"href": "/en/team/weltspielt"
},
+ {
+ "id": "saynax-jonas",
+ "name": "Saynax | Jonas",
+ "slug": "saynax-jonas",
+ "rank": "content",
+ "bio": "Handles infra, DevOps and security.",
+ "href": "/en/team/saynax-jonas"
+ },
{
"id": "open-content",
"name": "Your seat?",
@@ -97,6 +101,15 @@
"openPosition": true,
"applyUrl": "https://1lf.link/discord"
},
+ {
+ "id": "bavariankingdom",
+ "name": "BavarianKingdom",
+ "slug": "bavariankingdom",
+ "role": "Moderation",
+ "rank": "moderation",
+ "mcName": "morelia0815",
+ "href": "/en/team/bavariankingdom"
+ },
{
"id": "b3nny",
"name": "B3nNy",
@@ -113,6 +126,25 @@
"slogan": "Keep the community fair and friendly.",
"openPosition": true,
"applyUrl": "https://1lf.link/discord"
+ },
+ {
+ "id": "open-media",
+ "name": "Your seat?",
+ "role": "Media",
+ "rank": "media",
+ "slogan": "For YouTubers, streamers and anyone creating content around the server.",
+ "openPosition": true,
+ "applyUrl": "https://1lf.link/discord"
+ },
+ {
+ "id": "open-lite",
+ "name": "Lite Rank",
+ "role": "Lite",
+ "rank": "lite",
+ "slogan": "The Lite rank is granted only through voluntary donations via OpenCollective. Awarding it is not guaranteed.",
+ "openPosition": true,
+ "applyUrl": "https://opencollective.com/onelitefeather",
+ "applyVia": "opencollective"
}
]
}
diff --git a/i18n/locales/de.json b/i18n/locales/de.json
index 114b4a5..bfc4584 100644
--- a/i18n/locales/de.json
+++ b/i18n/locales/de.json
@@ -67,7 +67,7 @@
"team": {
"title": "Team",
"subtitle": "Lerne die Menschen hinter OneLiteFeather kennen.",
- "profile_title_fallback": "Team — OneLiteFeather",
+ "profile_title_fallback": "Team",
"profile_description_fallback": "Teammitglied-Profil bei OneLiteFeather.",
"search_label": "Team durchsuchen",
"search_placeholder": "Name suchen…",
@@ -84,8 +84,8 @@
"member_since": "Teil des Teams seit {year}",
"profile_link_aria": "{platform}-Profil von {name}",
"index": {
- "title": "Unser Team — OneLiteFeather",
- "subtitle": "Die Menschen hinter dem Netzwerk – und die freien Plätze, die auf dich warten.",
+ "title": "Unser Team",
+ "subtitle": "Die Menschen hinter dem Netzwerk und die freien Plätze, die auf dich warten.",
"description": "Lerne das OneLiteFeather-Team nach Rollen geordnet kennen und entdecke die offenen Stellen, auf die du dich bewerben kannst."
},
"ranks": {
@@ -93,13 +93,19 @@
"teamassist": "Teamassistenz",
"content": "Content",
"moderation": "Moderation",
- "media": "Social Media",
+ "media": "Media",
"lite": "Lite"
},
"open_position": {
"badge": "Offene Stelle",
"apply": "Über Discord bewerben",
- "apply_aria": "Für {role} über Discord bewerben"
+ "apply_aria": "Für {role} über Discord bewerben",
+ "apply_opencollective": "Über OpenCollective unterstützen",
+ "apply_aria_opencollective": "{role} über OpenCollective unterstützen"
+ },
+ "faq": {
+ "section_title": "Bewerbungen und Ränge",
+ "section_subtitle": "Antworten zu Ablauf, Anforderungen und dem Lite-Rang."
}
},
"timeline": {
diff --git a/i18n/locales/en.json b/i18n/locales/en.json
index 8210b06..d0501b5 100644
--- a/i18n/locales/en.json
+++ b/i18n/locales/en.json
@@ -67,7 +67,7 @@
"team": {
"title": "Team",
"subtitle": "Meet the people behind OneLiteFeather.",
- "profile_title_fallback": "Team — OneLiteFeather",
+ "profile_title_fallback": "Team",
"profile_description_fallback": "Team member profile on OneLiteFeather.",
"search_label": "Search team",
"search_placeholder": "Search name…",
@@ -84,8 +84,8 @@
"member_since": "Part of the team since {year}",
"profile_link_aria": "{platform} profile of {name}",
"index": {
- "title": "Our Team — OneLiteFeather",
- "subtitle": "The people behind the network — and the open seats waiting for you.",
+ "title": "Our Team",
+ "subtitle": "The people behind the network, and the open seats waiting for you.",
"description": "Meet the OneLiteFeather team, organised by role, and discover the open positions you can apply for."
},
"ranks": {
@@ -93,13 +93,19 @@
"teamassist": "Team Assistance",
"content": "Content",
"moderation": "Moderation",
- "media": "Social Media",
+ "media": "Media",
"lite": "Lite"
},
"open_position": {
"badge": "Open position",
"apply": "Apply via Discord",
- "apply_aria": "Apply for {role} via Discord"
+ "apply_aria": "Apply for {role} via Discord",
+ "apply_opencollective": "Support via OpenCollective",
+ "apply_aria_opencollective": "Support {role} via OpenCollective"
+ },
+ "faq": {
+ "section_title": "Applications and ranks",
+ "section_subtitle": "Answers on the process, requirements, and the Lite rank."
}
},
"timeline": {
diff --git a/nuxt.config.ts b/nuxt.config.ts
index fb62c3b..655dbd0 100644
--- a/nuxt.config.ts
+++ b/nuxt.config.ts
@@ -99,6 +99,12 @@ export default defineNuxtConfig({
{label: 'Last Modified', select: 'sitemap:lastmod', width: '25%'},
{label: 'Language', select: 'sitemap:hreflang', width: '25%'}
],
+ // Team profiles live in a data-type content collection so they're
+ // not auto-discovered. We materialise the per-member URLs through
+ // a Nitro endpoint that reads the same JSON the page uses.
+ sources: [
+ '/api/__sitemap__/team'
+ ],
defaults: {
changefreq: 'weekly',
priority: 0.8
diff --git a/pages/team/[slug].vue b/pages/team/[slug].vue
index 257521e..c86297d 100644
--- a/pages/team/[slug].vue
+++ b/pages/team/[slug].vue
@@ -1,18 +1,50 @@
@@ -98,7 +162,15 @@ useSchemaOrg(() => {
class="rounded-full bg-brand-100 dark:bg-brand-900/40 px-2.5 py-0.5 text-xs font-semibold text-brand-700 dark:text-brand-300"
>{{ rankLabel }}
- {{ member.role }}
+
+
+
"{{ member.slogan }}"
{{ t('team.member_since', { year: member.since }) }}
diff --git a/pages/team/index.vue b/pages/team/index.vue
index f2239cd..677433e 100644
--- a/pages/team/index.vue
+++ b/pages/team/index.vue
@@ -1,6 +1,7 @@