Antoine's vision of the credit page (#95)
Co-authored-by: Minh Vo <minh.vo2@mail.mcgill.ca> Co-authored-by: Antoine Phan <hoangtuan11102@gmail.com>
This commit is contained in:
committed by
GitHub
parent
680a6a61a4
commit
a3908745d7
@@ -35,6 +35,7 @@
|
|||||||
<NavButton href="/council">Meet the council</NavButton>
|
<NavButton href="/council">Meet the council</NavButton>
|
||||||
<NavButton href="/events">Events</NavButton>
|
<NavButton href="/events">Events</NavButton>
|
||||||
<NavButton href="/resources">Resources</NavButton>
|
<NavButton href="/resources">Resources</NavButton>
|
||||||
|
<NavButton href="/devteam">Dev Team</NavButton>
|
||||||
{#if isElectionTime}
|
{#if isElectionTime}
|
||||||
<NavButton href="/join">Join ECSESS</NavButton>
|
<NavButton href="/join">Join ECSESS</NavButton>
|
||||||
{/if}
|
{/if}
|
||||||
@@ -52,6 +53,7 @@
|
|||||||
<NavButton href="/council">Meet the council</NavButton>
|
<NavButton href="/council">Meet the council</NavButton>
|
||||||
<NavButton href="/events">Events</NavButton>
|
<NavButton href="/events">Events</NavButton>
|
||||||
<NavButton href="/resources">Resources</NavButton>
|
<NavButton href="/resources">Resources</NavButton>
|
||||||
|
<NavButton href="/devteam">Dev Team</NavButton>
|
||||||
{#if isElectionTime}
|
{#if isElectionTime}
|
||||||
<NavButton href="/join">Join ECSESS</NavButton>
|
<NavButton href="/join">Join ECSESS</NavButton>
|
||||||
{/if}
|
{/if}
|
||||||
|
|||||||
19
src/components/team/CommitDot.svelte
Normal file
19
src/components/team/CommitDot.svelte
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
let { active = false } = $props<{
|
||||||
|
active?: boolean;
|
||||||
|
}>();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div class="relative">
|
||||||
|
<div
|
||||||
|
class="bg-ecsess-500 relative inline-flex size-2.5 items-center justify-center rounded-full transition-all duration-200 {active
|
||||||
|
? 'bg-ecsess-200'
|
||||||
|
: 'bg-ecsess-500'}"
|
||||||
|
>
|
||||||
|
{#if active}
|
||||||
|
<div
|
||||||
|
class="bg-ecsess-100 relative inline-flex size-2.5 animate-ping items-center justify-center rounded-full"
|
||||||
|
></div>
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
38
src/components/team/ContribTimeline.svelte
Normal file
38
src/components/team/ContribTimeline.svelte
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import type { DevTeam } from '$lib/schemas';
|
||||||
|
import YearLine from './YearLine.svelte';
|
||||||
|
import DevCard from './DevCard.svelte';
|
||||||
|
|
||||||
|
let {
|
||||||
|
term,
|
||||||
|
members,
|
||||||
|
active = false
|
||||||
|
} = $props<{
|
||||||
|
term: string;
|
||||||
|
members: DevTeam[];
|
||||||
|
active?: boolean;
|
||||||
|
}>();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div class="relative w-full">
|
||||||
|
<!-- Branch Line with Year -->
|
||||||
|
<div class="mb-6">
|
||||||
|
<YearLine {term} {active} />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Members Grid -->
|
||||||
|
<div class="ml-8 grid grid-cols-1 gap-8 sm:grid-cols-2 lg:grid-cols-3">
|
||||||
|
{#each members as member}
|
||||||
|
<DevCard
|
||||||
|
name={member.name}
|
||||||
|
role={member.role}
|
||||||
|
year={member.yearProgram}
|
||||||
|
src={member.image}
|
||||||
|
funFact={member.funFact}
|
||||||
|
github={member.github}
|
||||||
|
email={member.email}
|
||||||
|
{active}
|
||||||
|
/>
|
||||||
|
{/each}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
126
src/components/team/DevCard.svelte
Normal file
126
src/components/team/DevCard.svelte
Normal file
@@ -0,0 +1,126 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import { Github, Mail } from '@lucide/svelte';
|
||||||
|
|
||||||
|
let {
|
||||||
|
name,
|
||||||
|
role,
|
||||||
|
year,
|
||||||
|
src,
|
||||||
|
funFact,
|
||||||
|
github,
|
||||||
|
email,
|
||||||
|
active = false
|
||||||
|
} = $props<{
|
||||||
|
name: string;
|
||||||
|
role: string;
|
||||||
|
year: string;
|
||||||
|
src: string;
|
||||||
|
funFact: string;
|
||||||
|
github: string;
|
||||||
|
email: string;
|
||||||
|
active?: boolean;
|
||||||
|
}>();
|
||||||
|
|
||||||
|
// Extract GitHub username from URL and construct avatar URL
|
||||||
|
let githubAvatar = $derived.by(() => {
|
||||||
|
if (!github) return src;
|
||||||
|
try {
|
||||||
|
const url = new URL(github);
|
||||||
|
const username = url.pathname.split('/').filter(Boolean)[0];
|
||||||
|
return username ? `https://github.com/${username}.png?size=200` : src;
|
||||||
|
} catch {
|
||||||
|
return src;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div class="fadeup group relative flex flex-col">
|
||||||
|
<!-- Commit Card -->
|
||||||
|
<div
|
||||||
|
class="hover:shadow-ecsess-black/50 relative w-full overflow-hidden rounded-xl transition-all duration-100 ease-linear hover:scale-101 hover:shadow-md {active
|
||||||
|
? 'bg-ecsess-800 shadow-ecsess-black shadow-sm'
|
||||||
|
: 'bg-ecsess-900 shadow-ecsess-black'}"
|
||||||
|
>
|
||||||
|
<!-- Header -->
|
||||||
|
<div class="px-6 pt-5 pb-3">
|
||||||
|
<div class="flex flex-col items-center gap-2.5 text-center">
|
||||||
|
<img src={githubAvatar} alt={name} class="size-20 shrink-0 rounded-full object-cover" />
|
||||||
|
<div class="w-full">
|
||||||
|
<h3 class="text-ecsess-100 text-lg font-semibold">
|
||||||
|
{name}
|
||||||
|
</h3>
|
||||||
|
<p class="text-ecsess-400 text-base">{role}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Body -->
|
||||||
|
<div class="space-y-3 px-6 pb-5">
|
||||||
|
<!-- Year Badge -->
|
||||||
|
<div class="flex items-center justify-center gap-2">
|
||||||
|
<span
|
||||||
|
class="rounded-full px-4 py-1.5 text-base font-medium {active
|
||||||
|
? 'bg-ecsess-900 text-ecsess-300'
|
||||||
|
: 'bg-ecsess-800 text-ecsess-400'}"
|
||||||
|
>
|
||||||
|
{year}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Fun Fact / Commit Message -->
|
||||||
|
<div class="min-h-[60px]">
|
||||||
|
<p class="text-ecsess-300 text-sm leading-relaxed italic">
|
||||||
|
{funFact || ``}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Actions -->
|
||||||
|
<div class="flex gap-3 pt-1">
|
||||||
|
{#if github}
|
||||||
|
<a
|
||||||
|
href={github}
|
||||||
|
target="_blank"
|
||||||
|
class="items-center justify-center gap-2 rounded-lg px-4 py-2.5 text-base font-medium transition-colors {active
|
||||||
|
? 'bg-ecsess-900 text-ecsess-300 hover:bg-ecsess-950'
|
||||||
|
: 'bg-ecsess-800 text-ecsess-400 hover:bg-ecsess-700'} {active && email
|
||||||
|
? 'flex flex-1'
|
||||||
|
: 'flex w-full'}"
|
||||||
|
>
|
||||||
|
<Github class="size-4.5" />
|
||||||
|
GitHub
|
||||||
|
</a>
|
||||||
|
{/if}
|
||||||
|
{#if email && active}
|
||||||
|
<a
|
||||||
|
href="mailto:{email}"
|
||||||
|
class="flex flex-1 items-center justify-center gap-2 rounded-lg px-4 py-2.5 text-base font-medium transition-colors {active
|
||||||
|
? 'bg-ecsess-900 text-ecsess-300 hover:bg-ecsess-950'
|
||||||
|
: 'bg-ecsess-800 text-ecsess-400 hover:bg-ecsess-700'}"
|
||||||
|
>
|
||||||
|
<Mail class="size-4.5" />
|
||||||
|
Email
|
||||||
|
</a>
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.fadeup {
|
||||||
|
animation: fadeUp both;
|
||||||
|
animation-timeline: view();
|
||||||
|
animation-range: entry 20% cover 35%;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes fadeUp {
|
||||||
|
from {
|
||||||
|
opacity: 0;
|
||||||
|
transform: translateY(20px);
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
opacity: 1;
|
||||||
|
transform: translateY(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
27
src/components/team/YearLine.svelte
Normal file
27
src/components/team/YearLine.svelte
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import CommitDot from './CommitDot.svelte';
|
||||||
|
|
||||||
|
let { term, active = false } = $props<{
|
||||||
|
term: string;
|
||||||
|
active?: boolean;
|
||||||
|
}>();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div class="relative mb-6 flex items-center gap-4">
|
||||||
|
<!-- Git Node column (w-3 = 12px, aligns with vertical timeline; circle centered) -->
|
||||||
|
<div class="flex w-3 shrink-0 justify-center">
|
||||||
|
<CommitDot {active} />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Year Label -->
|
||||||
|
<div
|
||||||
|
class="flex shrink-0 items-center justify-center font-mono text-xl font-bold tracking-wide uppercase transition-colors md:justify-start {active
|
||||||
|
? 'text-ecsess-300'
|
||||||
|
: 'text-ecsess-400'}"
|
||||||
|
>
|
||||||
|
<span>{term}</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Branch Line -->
|
||||||
|
<div class="h-px min-w-[100px] flex-1 {active ? 'bg-ecsess-500' : 'bg-ecsess-600'}"></div>
|
||||||
|
</div>
|
||||||
@@ -72,3 +72,15 @@ export type Redirect = {
|
|||||||
shortname: string;
|
shortname: string;
|
||||||
url: string;
|
url: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type DevTeam = {
|
||||||
|
name: string;
|
||||||
|
role: string;
|
||||||
|
yearProgram: string;
|
||||||
|
email: string;
|
||||||
|
active: boolean;
|
||||||
|
term: string;
|
||||||
|
funFact: string;
|
||||||
|
github: string;
|
||||||
|
image: string; //URL
|
||||||
|
};
|
||||||
|
|||||||
23
src/routes/devteam/+page.server.ts
Normal file
23
src/routes/devteam/+page.server.ts
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
//Wait for CMS to setup
|
||||||
|
import type { DevTeam } from '$lib/schemas';
|
||||||
|
import { getFromCMS } from '$lib/utils';
|
||||||
|
|
||||||
|
const query = `*[_type == "devTeam"]{
|
||||||
|
name,
|
||||||
|
role,
|
||||||
|
yearProgram,
|
||||||
|
email,
|
||||||
|
active,
|
||||||
|
term,
|
||||||
|
funFact,
|
||||||
|
github,
|
||||||
|
"image": image.asset->url+"?h=300&fm=webp",
|
||||||
|
}`;
|
||||||
|
|
||||||
|
export const load = async ({ url }) => {
|
||||||
|
let devTeam: DevTeam[] = await getFromCMS(query);
|
||||||
|
return {
|
||||||
|
devTeam: devTeam,
|
||||||
|
canonical: url.href
|
||||||
|
};
|
||||||
|
};
|
||||||
80
src/routes/devteam/+page.svelte
Normal file
80
src/routes/devteam/+page.svelte
Normal file
@@ -0,0 +1,80 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import SeoMetaTags from 'components/layout/SeoMetaTags.svelte';
|
||||||
|
import Section from 'components/layout/Section.svelte';
|
||||||
|
import ContribTimeline from 'components/team/ContribTimeline.svelte';
|
||||||
|
import Link from 'components/Link.svelte';
|
||||||
|
import type { DevTeam } from '$lib/schemas.js';
|
||||||
|
|
||||||
|
let { data } = $props();
|
||||||
|
|
||||||
|
let devTeam = $derived(data.devTeam ?? []);
|
||||||
|
|
||||||
|
// Extract the starting year from a term string "xxxx-xxxx"
|
||||||
|
function termYear(term: string): number {
|
||||||
|
return parseInt(term.split('-')[0], 10);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sort by Active (Current) first, then by term year (newest/latest first)
|
||||||
|
let sortedTeam = $derived(
|
||||||
|
[...devTeam].sort((a, b) => {
|
||||||
|
if (a.active && !b.active) return -1;
|
||||||
|
if (!a.active && b.active) return 1;
|
||||||
|
|
||||||
|
return termYear(b.term) - termYear(a.term);
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
function getGroup(member: DevTeam) {
|
||||||
|
return member.active ? 'Active team' : member.term;
|
||||||
|
}
|
||||||
|
|
||||||
|
let groupedTeam = $derived(
|
||||||
|
sortedTeam.reduce<{ group: string; members: DevTeam[]; active: boolean }[]>((acc, member) => {
|
||||||
|
const group = getGroup(member);
|
||||||
|
const last = acc[acc.length - 1];
|
||||||
|
if (last && last.group === group) {
|
||||||
|
last.members.push(member);
|
||||||
|
} else {
|
||||||
|
acc.push({ group, members: [member], active: member.active });
|
||||||
|
}
|
||||||
|
return acc;
|
||||||
|
}, [])
|
||||||
|
);
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<SeoMetaTags />
|
||||||
|
|
||||||
|
<Section from="from-ecsess-black" to="to-ecsess-black" via="via-ecsess-800" direction="to-b">
|
||||||
|
<div class="relative flex h-full w-full flex-col items-center">
|
||||||
|
<!-- Hero -->
|
||||||
|
<div class="my-8">
|
||||||
|
<span class="page-title"> git log --dev-team </span>
|
||||||
|
<p class="text-ecsess-300 mt-6 font-mono text-lg">
|
||||||
|
Want to build the future of ECSESS? <br />
|
||||||
|
<Link href="https://github.com/mcgill-ecsess/ECSESS" external>
|
||||||
|
<span
|
||||||
|
class="text-ecsess-400 decoration-ecsess-500 hover:text-ecsess-300 hover:decoration-ecsess-400 font-semibold underline decoration-2 underline-offset-4 transition-all"
|
||||||
|
>
|
||||||
|
Contribute on GitHub →
|
||||||
|
</span>
|
||||||
|
</Link>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Git Tree History -->
|
||||||
|
<div class="relative w-full max-w-6xl px-4">
|
||||||
|
<div class="relative mx-auto max-w-fit">
|
||||||
|
<!-- Main vertical timeline connector (centered in 12px node column, aligns with GitNode circles) -->
|
||||||
|
|
||||||
|
<!-- Cohort Branches -->
|
||||||
|
<div class="space-y-16">
|
||||||
|
{#each groupedTeam as { group, members, active }}
|
||||||
|
<div class="relative">
|
||||||
|
<ContribTimeline term={group} {members} {active} />
|
||||||
|
</div>
|
||||||
|
{/each}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Section>
|
||||||
Reference in New Issue
Block a user