Compare commits
No commits in common. "972a94f5dc1a814467230539ee26c884fbed2f1f" and "3dd4f2cc2176028aadadf34ed5d5b3c6a3b8cb59" have entirely different histories.
972a94f5dc
...
3dd4f2cc21
|
@ -1,4 +1,4 @@
|
||||||
export type MemberCardProps = {
|
type MemberCardProps = {
|
||||||
id: string
|
id: string
|
||||||
icon: string
|
icon: string
|
||||||
name: string
|
name: string
|
||||||
|
@ -22,3 +22,26 @@ export function MemberCard(
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const memberMock: MemberCardProps[] = Array(50).fill(undefined).map(
|
||||||
|
(_, index) => {
|
||||||
|
return {
|
||||||
|
name: `Michel ${randomLastName()}`,
|
||||||
|
groups: ['FabManager', 'Étudiant'],
|
||||||
|
icon: `url("https://thispersondoesnotexist.com/")`,
|
||||||
|
id: String(index),
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
function randomLastName() {
|
||||||
|
const randomArray = Math.round(Math.random() * 1e8).toString().split('').map(
|
||||||
|
Number,
|
||||||
|
)
|
||||||
|
|
||||||
|
const [first, ...tail] = randomArray.map((number) =>
|
||||||
|
String.fromCodePoint(number + 97)
|
||||||
|
)
|
||||||
|
|
||||||
|
return [first.toLocaleUpperCase(), ...tail].join('')
|
||||||
|
}
|
||||||
|
|
|
@ -1,46 +1,62 @@
|
||||||
import { BlogCard, BlogProps } from ':components/BlogBlocks.tsx'
|
import { BlogCard, BlogProps } from ':components/BlogBlocks.tsx'
|
||||||
import CardList from ':islands/CardList.tsx'
|
import Suspense from ':islands/Suspens.tsx'
|
||||||
import type { Ref } from 'preact'
|
import { requestApiStream } from ':src/utils.ts'
|
||||||
|
import { Signal, useSignal } from '@preact/signals'
|
||||||
|
import type { JSX } from 'preact'
|
||||||
|
import { useEffect } from 'preact/hooks'
|
||||||
|
|
||||||
|
function fillList(
|
||||||
|
list: Signal<JSX.Element[]>,
|
||||||
|
{ limit, ac }: { limit?: number; ac?: AbortController },
|
||||||
|
) {
|
||||||
|
;(async () => {
|
||||||
|
const newsList = requestApiStream<void, BlogProps>(
|
||||||
|
'news/fetchAll',
|
||||||
|
'GET',
|
||||||
|
)
|
||||||
|
|
||||||
|
for await (const news of newsList) {
|
||||||
|
list.value = [
|
||||||
|
...list.value,
|
||||||
|
BlogCard({ ...news, lastUpdate: new Date(news.lastUpdate) }),
|
||||||
|
]
|
||||||
|
if (limit && list.value.length >= limit) break
|
||||||
|
}
|
||||||
|
ac?.abort()
|
||||||
|
})()
|
||||||
|
}
|
||||||
|
|
||||||
export default function BlogCardList(
|
export default function BlogCardList(
|
||||||
{ limit, usePlaceholder, useObserver }: {
|
{ limit, usePlaceholder }: { usePlaceholder?: boolean; limit?: number },
|
||||||
limit?: number
|
|
||||||
usePlaceholder?: boolean
|
|
||||||
useObserver?: boolean
|
|
||||||
},
|
|
||||||
) {
|
) {
|
||||||
if (usePlaceholder) {
|
const list = useSignal<JSX.Element[]>([])
|
||||||
return (
|
const ac = new AbortController()
|
||||||
<CardList
|
|
||||||
apiRoute='news/fetchAll'
|
useEffect(() => {
|
||||||
builder={builder}
|
fillList(list, { limit, ac })
|
||||||
limit={limit}
|
})
|
||||||
placeholder={Placeholder}
|
|
||||||
fallback={Fallback}
|
if (limit && usePlaceholder) {
|
||||||
useObserver={useObserver}
|
const placeholders = Array
|
||||||
/>
|
.from({ length: limit })
|
||||||
)
|
.map((_, index) => (
|
||||||
|
<Suspense
|
||||||
|
loader={<Placeholder />}
|
||||||
|
fallback={Fallback}
|
||||||
|
signal={ac.signal}
|
||||||
|
>
|
||||||
|
{updateFromList(list, index)}
|
||||||
|
</Suspense>
|
||||||
|
))
|
||||||
|
return <>{placeholders}</>
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return <>{list}</>
|
||||||
<CardList
|
|
||||||
apiRoute='news/fetchAll'
|
|
||||||
builder={builder}
|
|
||||||
limit={limit}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function builder(news: BlogProps) {
|
function Placeholder() {
|
||||||
return BlogCard({ ...news, lastUpdate: new Date(news.lastUpdate) })
|
|
||||||
}
|
|
||||||
|
|
||||||
function Placeholder({ ref }: { ref?: Ref<HTMLDivElement> | undefined }) {
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div class='components__blog_block components__blog_block--card components__blog_block--placeholder'>
|
||||||
class='components__blog_block components__blog_block--card components__blog_block--placeholder'
|
|
||||||
ref={ref}
|
|
||||||
>
|
|
||||||
<h3>Chargement ...</h3>
|
<h3>Chargement ...</h3>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
@ -56,3 +72,18 @@ function Fallback() {
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function updateFromList(
|
||||||
|
list: Signal<JSX.Element[]>,
|
||||||
|
index: number,
|
||||||
|
): Promise<JSX.Element> {
|
||||||
|
const { promise, resolve } = Promise.withResolvers<JSX.Element>()
|
||||||
|
list.subscribe((value: JSX.Element[]) => {
|
||||||
|
const selected = value.at(index)
|
||||||
|
if (selected) {
|
||||||
|
resolve(selected)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
return promise
|
||||||
|
}
|
||||||
|
|
|
@ -1,103 +0,0 @@
|
||||||
import Suspense, { Fallback } from ':islands/Suspens.tsx'
|
|
||||||
import { requestApiStream } from ':src/utils.ts'
|
|
||||||
import { Signal, useSignal } from '@preact/signals'
|
|
||||||
import type { JSX, Ref } from 'preact'
|
|
||||||
import { useEffect, useRef } from 'preact/hooks'
|
|
||||||
|
|
||||||
export type Builder<T> = (props: T) => JSX.Element
|
|
||||||
|
|
||||||
export type CardListProps<ApiResponse, RefType = null> = {
|
|
||||||
apiRoute: string
|
|
||||||
builder: Builder<ApiResponse>
|
|
||||||
limit?: number
|
|
||||||
} | {
|
|
||||||
apiRoute: string
|
|
||||||
builder: Builder<ApiResponse>
|
|
||||||
limit?: number
|
|
||||||
useObserver?: boolean
|
|
||||||
placeholder: ({ ref }: { ref: Ref<RefType> | undefined }) => JSX.Element
|
|
||||||
fallback: Fallback
|
|
||||||
}
|
|
||||||
|
|
||||||
export default function CardList<ApiResponse, RefType = null>(
|
|
||||||
{ limit, builder, apiRoute, ...props }: CardListProps<ApiResponse, RefType>,
|
|
||||||
) {
|
|
||||||
const list: Signal<JSX.Element[]> = useSignal<JSX.Element[]>([])
|
|
||||||
const ac = new AbortController()
|
|
||||||
const ref = useRef(null)
|
|
||||||
|
|
||||||
const useObserver = 'useObserver' in props ? props.useObserver : false
|
|
||||||
const placeholder = 'placeholder' in props ? props.placeholder : false
|
|
||||||
const fallback = 'fallback' in props ? props.fallback : false
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (ref.current && useObserver) {
|
|
||||||
const observer = new IntersectionObserver(([entry]) => {
|
|
||||||
if (entry.isIntersecting) {
|
|
||||||
fillList(list, builder, apiRoute, { limit, ac })
|
|
||||||
observer.disconnect()
|
|
||||||
}
|
|
||||||
}, {
|
|
||||||
rootMargin: '300px',
|
|
||||||
})
|
|
||||||
observer.observe(ref.current)
|
|
||||||
} else {
|
|
||||||
fillList(list, builder, apiRoute, { limit, ac })
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
if (limit && placeholder && fallback) {
|
|
||||||
const placeholders = Array
|
|
||||||
.from({ length: limit })
|
|
||||||
.map((_, index) => (
|
|
||||||
<Suspense
|
|
||||||
loader={placeholder({ ref: index === 0 ? ref : undefined })}
|
|
||||||
fallback={fallback}
|
|
||||||
signal={ac.signal}
|
|
||||||
>
|
|
||||||
{updateFromList(list, index)}
|
|
||||||
</Suspense>
|
|
||||||
))
|
|
||||||
return <>{placeholders}</>
|
|
||||||
}
|
|
||||||
|
|
||||||
return <>{list}</>
|
|
||||||
}
|
|
||||||
|
|
||||||
function fillList<ApiResponse>(
|
|
||||||
list: Signal<JSX.Element[]>,
|
|
||||||
builder: Builder<ApiResponse>,
|
|
||||||
apiRoute: string,
|
|
||||||
{ limit, ac }: { limit?: number; ac?: AbortController },
|
|
||||||
) {
|
|
||||||
;(async () => {
|
|
||||||
const propsList = requestApiStream<void, ApiResponse>(
|
|
||||||
apiRoute,
|
|
||||||
'GET',
|
|
||||||
)
|
|
||||||
|
|
||||||
for await (const props of propsList) {
|
|
||||||
list.value = [
|
|
||||||
...list.value,
|
|
||||||
builder(props),
|
|
||||||
]
|
|
||||||
if (limit && list.value.length >= limit) break
|
|
||||||
}
|
|
||||||
ac?.abort()
|
|
||||||
})()
|
|
||||||
}
|
|
||||||
|
|
||||||
function updateFromList(
|
|
||||||
list: Signal<JSX.Element[]>,
|
|
||||||
index: number,
|
|
||||||
): Promise<JSX.Element> {
|
|
||||||
const { promise, resolve } = Promise.withResolvers<JSX.Element>()
|
|
||||||
list.subscribe((value: JSX.Element[]) => {
|
|
||||||
const selected = value.at(index)
|
|
||||||
if (selected) {
|
|
||||||
resolve(selected)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
return promise
|
|
||||||
}
|
|
|
@ -1,60 +0,0 @@
|
||||||
import { MemberCard } from ':components/MemberCard.tsx'
|
|
||||||
import CardList from ':islands/CardList.tsx'
|
|
||||||
import type { Ref } from 'preact'
|
|
||||||
|
|
||||||
export default function MemberCardList(
|
|
||||||
{ limit, filters, usePlaceholder, useObserver }: {
|
|
||||||
filters?: [string, string][]
|
|
||||||
limit?: number
|
|
||||||
usePlaceholder?: boolean
|
|
||||||
useObserver?: boolean
|
|
||||||
},
|
|
||||||
) {
|
|
||||||
const query = new URL('members/fetchAll', 'https://null/')
|
|
||||||
filters?.forEach((filter) => query.searchParams.set(...filter))
|
|
||||||
|
|
||||||
const apiRoute = `${query.pathname}${query.search}`
|
|
||||||
|
|
||||||
if (usePlaceholder) {
|
|
||||||
return (
|
|
||||||
<CardList
|
|
||||||
apiRoute={apiRoute}
|
|
||||||
builder={MemberCard}
|
|
||||||
limit={limit}
|
|
||||||
placeholder={Placeholder}
|
|
||||||
fallback={Fallback}
|
|
||||||
useObserver={useObserver}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<CardList
|
|
||||||
apiRoute={apiRoute}
|
|
||||||
builder={MemberCard}
|
|
||||||
limit={limit}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
function Placeholder({ ref }: { ref?: Ref<HTMLDivElement> | undefined }) {
|
|
||||||
return (
|
|
||||||
<div
|
|
||||||
// class='components__blog_block components__blog_block--card components__blog_block--placeholder'
|
|
||||||
ref={ref}
|
|
||||||
>
|
|
||||||
<h3>Chargement ...</h3>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
function Fallback() {
|
|
||||||
return (
|
|
||||||
<div
|
|
||||||
// class='components__blog_block components__blog_block--card components__blog_block--fallback'
|
|
||||||
inert
|
|
||||||
>
|
|
||||||
<h3>Pas d'utilisateur</h3>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
|
@ -16,7 +16,7 @@ function RenderError(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export type Fallback = ({ error }: { error: Error }) => JSX.Element
|
type Fallback = ({ error }: { error: Error }) => JSX.Element
|
||||||
|
|
||||||
export default function Suspense(
|
export default function Suspense(
|
||||||
{ loader, fallback, signal, children }: {
|
{ loader, fallback, signal, children }: {
|
||||||
|
|
|
@ -1,28 +0,0 @@
|
||||||
import { MemberCardProps } from ':components/MemberCard.tsx'
|
|
||||||
import { db } from ':src/db/mod.ts'
|
|
||||||
import { dbToMemberCardProps } from ':src/members/mod.ts'
|
|
||||||
import { SessionHandlers } from ':src/session/mod.ts'
|
|
||||||
import { respondApi, respondApiStream } from ':src/utils.ts'
|
|
||||||
|
|
||||||
export const handler: SessionHandlers = {
|
|
||||||
GET(_req, ctx) {
|
|
||||||
try {
|
|
||||||
const memberList = dbToMemberCardProps(db)
|
|
||||||
|
|
||||||
const params = ctx.url.searchParams
|
|
||||||
|
|
||||||
const groupParam = params.get('group')
|
|
||||||
if (groupParam) {
|
|
||||||
const list = memberList.filter(
|
|
||||||
(member) => member.groups.includes(groupParam),
|
|
||||||
) as AsyncIterableIterator<MemberCardProps>
|
|
||||||
|
|
||||||
return respondApiStream(list)
|
|
||||||
}
|
|
||||||
|
|
||||||
return respondApiStream(memberList)
|
|
||||||
} catch (error) {
|
|
||||||
return respondApi('error', error)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
}
|
|
|
@ -3,10 +3,10 @@ import { AutoGrid } from ':components/AutoGrid.tsx'
|
||||||
import { CohabitInfoTable } from ':components/CohabitInfoTable.tsx'
|
import { CohabitInfoTable } from ':components/CohabitInfoTable.tsx'
|
||||||
import { Heros } from ':components/Heros.tsx'
|
import { Heros } from ':components/Heros.tsx'
|
||||||
import { MachineCard, machineMock } from ':components/MachineCard.tsx'
|
import { MachineCard, machineMock } from ':components/MachineCard.tsx'
|
||||||
|
import { MemberCard, memberMock } from ':components/MemberCard.tsx'
|
||||||
import { ProjectCard, projectMock } from ':components/ProjectCard.tsx'
|
import { ProjectCard, projectMock } from ':components/ProjectCard.tsx'
|
||||||
import { SponsorCards } from ':components/SponsorCards.tsx'
|
import { SponsorCards } from ':components/SponsorCards.tsx'
|
||||||
import BlogCardList from ':islands/BlogCardList.tsx'
|
import BlogCardList from ':islands/BlogCardList.tsx'
|
||||||
import MemberCardList from ':islands/MemberCardList.tsx'
|
|
||||||
|
|
||||||
export default function Home() {
|
export default function Home() {
|
||||||
return (
|
return (
|
||||||
|
@ -18,12 +18,10 @@ export default function Home() {
|
||||||
<section id='first-section'>
|
<section id='first-section'>
|
||||||
<h2>Nos actus</h2>
|
<h2>Nos actus</h2>
|
||||||
<AutoGrid columnWidth='15rem' style={{ alignItems: 'center' }}>
|
<AutoGrid columnWidth='15rem' style={{ alignItems: 'center' }}>
|
||||||
<BlogCardList
|
<>
|
||||||
limit={4}
|
<BlogCardList limit={4} usePlaceholder={true} />
|
||||||
usePlaceholder={true}
|
<a href='/blog' class='cta'>Voir plus</a>
|
||||||
useObserver={true}
|
</>
|
||||||
/>
|
|
||||||
<a href='/blog' class='cta'>Voir plus</a>
|
|
||||||
</AutoGrid>
|
</AutoGrid>
|
||||||
</section>
|
</section>
|
||||||
<section>
|
<section>
|
||||||
|
@ -52,12 +50,10 @@ export default function Home() {
|
||||||
<section>
|
<section>
|
||||||
<h2>Nos membres</h2>
|
<h2>Nos membres</h2>
|
||||||
<AutoGrid columnWidth='15rem' style={{ alignItems: 'center' }}>
|
<AutoGrid columnWidth='15rem' style={{ alignItems: 'center' }}>
|
||||||
<MemberCardList
|
<>
|
||||||
limit={4}
|
{memberMock.slice(0, 4).map(MemberCard)}
|
||||||
usePlaceholder={true}
|
<a href='/membres' class='cta'>Nous découvrir</a>
|
||||||
useObserver={true}
|
</>
|
||||||
/>
|
|
||||||
<a href='/membres' class='cta'>Nous découvrir</a>
|
|
||||||
</AutoGrid>
|
</AutoGrid>
|
||||||
</section>
|
</section>
|
||||||
<section>
|
<section>
|
||||||
|
|
|
@ -1,19 +1,31 @@
|
||||||
import { PageProps } from '$fresh/server.ts'
|
import { PageProps } from '$fresh/server.ts'
|
||||||
import { Markdown } from ':components/Markdown.tsx'
|
import { Markdown } from ':components/Markdown.tsx'
|
||||||
import { MemberCard } from ':components/MemberCard.tsx'
|
import { MemberCard, memberMock } from ':components/MemberCard.tsx'
|
||||||
import { db } from ':src/db/mod.ts'
|
|
||||||
import { fetchCarnet, userToMemberCardProps } from ':src/members/mod.ts'
|
|
||||||
|
|
||||||
export default async function Member(_: Request, { params, url }: PageProps) {
|
const db = [
|
||||||
const uuid = params.id as ReturnType<Crypto['randomUUID']>
|
'julien.oculi',
|
||||||
const user = await db.ressource.user.get({ uuid }).catch(() => undefined)
|
]
|
||||||
|
|
||||||
if (!user) {
|
async function getCarnet(user: string): Promise<string> {
|
||||||
|
try {
|
||||||
|
const response = await fetch(
|
||||||
|
`https://git.cohabit.fr/${user}/.carnet/raw/branch/main/index.md`,
|
||||||
|
)
|
||||||
|
return response.text()
|
||||||
|
} catch (error) {
|
||||||
|
return 'Carnet introuvable\n```js\nString(error)\n```'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default async function Member(_: Request, { params }: PageProps) {
|
||||||
|
const id = Number(params.id)
|
||||||
|
const Member = memberMock.at(id)
|
||||||
|
|
||||||
|
if (!Member) {
|
||||||
return <h3>Membre inconnu, peut être serez vous le prochain</h3>
|
return <h3>Membre inconnu, peut être serez vous le prochain</h3>
|
||||||
}
|
}
|
||||||
|
|
||||||
const memberCardProps = await userToMemberCardProps(user)
|
const carnet = await getCarnet(db.at(id)!)
|
||||||
const carnet = await fetchCarnet(user.login)
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
|
@ -26,27 +38,17 @@ export default async function Member(_: Request, { params, url }: PageProps) {
|
||||||
>
|
>
|
||||||
<style>{'.markdown-body { max-width: 80dvw; }'}</style>
|
<style>{'.markdown-body { max-width: 80dvw; }'}</style>
|
||||||
<div>
|
<div>
|
||||||
{MemberCard(memberCardProps)}
|
{MemberCard(Member)}
|
||||||
<br />
|
<br />
|
||||||
<a
|
<a
|
||||||
href={`https://${
|
href={`/membres/${id}/portfolio/index.html`}
|
||||||
user.login.replace('.', '-')
|
|
||||||
}.portfolio.${url.hostname}`}
|
|
||||||
class='cta'
|
class='cta'
|
||||||
target='_blank'
|
target='_blank'
|
||||||
>
|
>
|
||||||
Portfolio
|
Portfolio
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
<Markdown
|
<Markdown>{carnet}</Markdown>
|
||||||
options={{
|
|
||||||
baseUrl:
|
|
||||||
`https://git.cohabit.fr/${user.login}/.carnet/raw/branch/main/index.md`,
|
|
||||||
allowMath: true,
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{carnet}
|
|
||||||
</Markdown>
|
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { AutoGrid } from ':components/AutoGrid.tsx'
|
import { AutoGrid } from ':components/AutoGrid.tsx'
|
||||||
import MemberCardList from ':islands/MemberCardList.tsx'
|
import { MemberCard, memberMock } from ':components/MemberCard.tsx'
|
||||||
|
|
||||||
export default function Membres() {
|
export default function Membres() {
|
||||||
return (
|
return (
|
||||||
|
@ -8,34 +8,32 @@ export default function Membres() {
|
||||||
<section>
|
<section>
|
||||||
<h2>Permanents</h2>
|
<h2>Permanents</h2>
|
||||||
<AutoGrid columnWidth='15rem'>
|
<AutoGrid columnWidth='15rem'>
|
||||||
<MemberCardList
|
{memberMock.slice(0, 5).map(MemberCard)}
|
||||||
filters={[['group', 'user.admin']]}
|
|
||||||
usePlaceholder={true}
|
|
||||||
useObserver={true}
|
|
||||||
/>
|
|
||||||
</AutoGrid>
|
</AutoGrid>
|
||||||
</section>
|
</section>
|
||||||
<section>
|
<section>
|
||||||
<h2>Bénévoles</h2>
|
<h2>Bénévoles</h2>
|
||||||
<AutoGrid columnWidth='15rem'>
|
<AutoGrid columnWidth='15rem'>
|
||||||
<MemberCardList
|
{memberMock.slice(5, 10).map(MemberCard)}
|
||||||
filters={[['group', 'user.member']]}
|
|
||||||
usePlaceholder={true}
|
|
||||||
useObserver={true}
|
|
||||||
/>
|
|
||||||
</AutoGrid>
|
</AutoGrid>
|
||||||
</section>
|
</section>
|
||||||
<section>
|
<section>
|
||||||
<h2>Service civique</h2>
|
<h2>Service civique</h2>
|
||||||
{/* <AutoGrid columnWidth='15rem'></AutoGrid> */}
|
<AutoGrid columnWidth='15rem'>
|
||||||
|
{memberMock.slice(10, 15).map(MemberCard)}
|
||||||
|
</AutoGrid>
|
||||||
</section>
|
</section>
|
||||||
<section>
|
<section>
|
||||||
<h2>Stage</h2>
|
<h2>Stage</h2>
|
||||||
{/* <AutoGrid columnWidth='15rem'></AutoGrid> */}
|
<AutoGrid columnWidth='15rem'>
|
||||||
|
{memberMock.slice(15, 20).map(MemberCard)}
|
||||||
|
</AutoGrid>
|
||||||
</section>
|
</section>
|
||||||
<section>
|
<section>
|
||||||
<h2>Étudiants</h2>
|
<h2>Étudiants</h2>
|
||||||
{/* <AutoGrid columnWidth='15rem'></AutoGrid> */}
|
<AutoGrid columnWidth='15rem'>
|
||||||
|
{memberMock.slice(0, 5).map(MemberCard)}
|
||||||
|
</AutoGrid>
|
||||||
</section>
|
</section>
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
|
|
|
@ -1,45 +0,0 @@
|
||||||
import { MemberCardProps } from ':components/MemberCard.tsx'
|
|
||||||
import { db } from ':src/db/mod.ts'
|
|
||||||
import { Db, Ref, User } from '@cohabit/ressources_manager/mod.ts'
|
|
||||||
|
|
||||||
export async function fetchCarnet(login: string): Promise<string> {
|
|
||||||
try {
|
|
||||||
const response = await fetch(
|
|
||||||
`https://git.cohabit.fr/${login}/.carnet/raw/branch/main/index.md`,
|
|
||||||
)
|
|
||||||
|
|
||||||
if (!response.ok) {
|
|
||||||
throw new Error(`[${response.status}] "${response.statusText}"`)
|
|
||||||
}
|
|
||||||
|
|
||||||
return response.text()
|
|
||||||
} catch (error) {
|
|
||||||
return `# Carnet introuvable\n\`\`\`js\n${String(error)}\n\`\`\``
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const resolver = Ref.dbResolver(db)
|
|
||||||
|
|
||||||
export async function userToMemberCardProps(user: User) {
|
|
||||||
const groupNames = user.groups.map(async (group) => {
|
|
||||||
const resolved = await group.ref(resolver)
|
|
||||||
return resolved.name
|
|
||||||
})
|
|
||||||
|
|
||||||
return {
|
|
||||||
name: user.name,
|
|
||||||
groups: await Promise.all(groupNames),
|
|
||||||
icon: `url("${user.avatar}")`,
|
|
||||||
id: user.uuid,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export function dbToMemberCardProps(
|
|
||||||
db: Db,
|
|
||||||
): AsyncIterableIterator<MemberCardProps> {
|
|
||||||
const memberList = db.ressource.user
|
|
||||||
.list()
|
|
||||||
.map(userToMemberCardProps)
|
|
||||||
|
|
||||||
return memberList as AsyncIterableIterator<MemberCardProps>
|
|
||||||
}
|
|
Loading…
Reference in a new issue