Skip to content

Commit

Permalink
feat(frontend): allow presets to be explored in dedicated page (#1232)
Browse files Browse the repository at this point in the history
* feat(backend): allowing auth users to create presents

* fix: linter&preset behaviour

* fix: remove unused import

* feat: improve presets exploration

* fix: remove platform presets list

* fix: export name field

* fix: platform icon

* fix: PR comments

* fix: tailwindcss classes
  • Loading branch information
axel7083 authored Jun 18, 2024
1 parent 983cc55 commit ee9abf7
Show file tree
Hide file tree
Showing 5 changed files with 140 additions and 3 deletions.
12 changes: 12 additions & 0 deletions frontend/src/app/(navfooter)/preset/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { Presets } from "@/app/(navfooter)/preset/presets"
import { get } from "@/lib/api/request"

export default async function Page() {
const compilers = await get("/compiler")

return (
<main className="mx-auto w-full max-w-3xl p-4">
<Presets serverCompilers={compilers}/>
</main>
)
}
36 changes: 36 additions & 0 deletions frontend/src/app/(navfooter)/preset/presets.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
"use client"

import { useState } from "react"

import PlatformSelect from "@/components/PlatformSelect"
import { PresetList } from "@/components/PresetList"
import * as api from "@/lib/api"

export function Presets({ serverCompilers }: {
serverCompilers: {
platforms: {
[id: string]: api.Platform
}
compilers: {
[id: string]: api.Compiler
}
}
}) {

const platforms = Object.keys(serverCompilers.platforms)

const [platform, setPlatform] = useState<string>(platforms.length > 0 ? platforms[0] : "")

return (
<section>
<h2 className="pb-2 text-lg font-medium tracking-tight">Platforms</h2>
<PlatformSelect
platforms={serverCompilers.platforms}
value={platform}
onChange={setPlatform}
/>
<h2 className="py-2 text-lg font-medium tracking-tight">Presets</h2>
<PresetList url={`/preset?platform=${platform}`}/>
</section>
)
}
20 changes: 18 additions & 2 deletions frontend/src/components/PlatformSelect/PlatformIcon.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
import Link from "next/link"

import { platformUrl } from "@/lib/api/urls"

import LogoDreamcast from "./dreamcast.svg"
import LogoGBA from "./gba.svg"
import LogoGCWii from "./gc_wii.svg"
Expand Down Expand Up @@ -39,15 +43,27 @@ export const PLATFORMS = Object.keys(ICONS)
export type Props = {
platform: string
className?: string
clickable?: boolean
size?: string | number
}

export function platformIcon(platform: string) {
return ICONS[platform as keyof typeof ICONS] || UnknownIcon
}

export function PlatformIcon({ platform, className, size }: Props) {
export function PlatformIcon({ platform, className, clickable, size }: Props) {
const Icon = platformIcon(platform)
const url = platformUrl(platform)

return <Icon width={size} height={size} className={className} />
if (clickable) {
return (
<Link href={url}>
<Icon width={size} height={size} className={className} />
</Link>
)
} else {
return (
<Icon width={size} height={size} className={className} />
)
}
}
2 changes: 1 addition & 1 deletion frontend/src/components/PlatformSelect/PlatformSelect.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ export default function PlatformSelect({ platforms, value, onChange, className }
className={classNames(styles.platform, { [styles.selected]: value === key })}
onClick={() => onChange(key)}
>
<PlatformIcon platform={key} />
<PlatformIcon clickable={false} platform={key} />
<div className={styles.labelContainer}>
<div className={styles.consoleName}>{platform.name}</div>
<div className={styles.platformName}>{platform.description}</div>
Expand Down
73 changes: 73 additions & 0 deletions frontend/src/components/PresetList.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
"use client"

import type { ReactNode, JSX } from "react"

import Link from "next/link"

import classNames from "classnames"

import AsyncButton from "@/components/AsyncButton"
import Button from "@/components/Button"
import LoadingSpinner from "@/components/loading.svg"
import { PlatformIcon } from "@/components/PlatformSelect/PlatformIcon"
import { type Preset, usePaginated } from "@/lib/api"
import { presetUrl } from "@/lib/api/urls"
import useTranslation from "@/lib/i18n/translate"

export interface Props {
url?: string
className?: string
item?: ({ preset }: { preset: Preset }) => JSX.Element
emptyButtonLabel?: ReactNode
}

export function PresetList({ url, className, item, emptyButtonLabel }: Props): JSX.Element {
const { results, isLoading, hasNext, loadNext } = usePaginated<Preset>(url || "/preset")
if (results.length === 0 && isLoading) {
return <div className={classNames("flex justify-center items-center gap-[0.5em] p-[1em] opacity-50", className)}>
<LoadingSpinner width="1.5em" height="1.5em" />
Just a moment...
</div>
}

const Item = item ?? PresetItem

return (
<ul className={classNames("flex flex-col justify-center gap-[0.5em] overflow-hidden rounded-md border-gray-6 text-sm", className)}>
{results.map(preset => (
<Item hideIcon key={preset.id} preset={preset} />
))}
{results.length === 0 && emptyButtonLabel && <li className={"col-[span_var(--num-columns,_1)] mt-[0.5em] flex items-center justify-center opacity-70"}>
<Link href="/new">

<Button>
{emptyButtonLabel}
</Button>

</Link>
</li>}
{hasNext && <li className={"col-[span_var(--num-columns,_1)] mt-[0.5em] flex items-center justify-center opacity-70"}>
<AsyncButton onClick={loadNext}>
Show more
</AsyncButton>
</li>}
</ul>
)
}

export function PresetItem({ preset, hideIcon }: { preset: Preset, hideIcon?: boolean }): JSX.Element {
const compilersTranslation = useTranslation("compilers")
const compilerName = compilersTranslation.t(preset.compiler)

return (
<div className="rounded-md border border-gray-6 p-[1em] text-sm">
<div className="flex items-center gap-2">
{hideIcon && <PlatformIcon platform={preset.platform} className="w-[1.2em]"/>}
<a className="font-semibold hover:text-[var(--link)]" href={presetUrl(preset)}>
{preset.name}
</a>
</div>
<p className="text-gray-11">{compilerName}</p>
</div>
)
}

0 comments on commit ee9abf7

Please sign in to comment.