mirror of
https://github.com/jorgev259/soc_site-astro.git
synced 2025-06-29 07:57:41 +00:00
Compare commits
6 commits
26c5fd2b03
...
427f271375
| Author | SHA1 | Date | |
|---|---|---|---|
| 427f271375 | |||
| 64c28f2727 | |||
| 676324fcd8 | |||
| 3b1cd90895 | |||
| 8fd7f7dece | |||
| 7dcd39f106 |
15 changed files with 452 additions and 18 deletions
|
|
@ -49,15 +49,11 @@ export default defineConfig({
|
|||
adapter: node({ mode: 'standalone' }),
|
||||
redirects: {
|
||||
'/en/[...params]': '/[...params]',
|
||||
'/anim/[id]': { status: 307, destination: '/maintenance' },
|
||||
'/game/[slug]': { status: 307, destination: '/maintenance' },
|
||||
'/platform/list': { status: 307, destination: '/maintenance' },
|
||||
'/platform/[id]': { status: 307, destination: '/maintenance' },
|
||||
'/profile': { status: 307, destination: '/maintenance' },
|
||||
'/profile/[username]': { status: 307, destination: '/maintenance' },
|
||||
'/series/[slug]': { status: 307, destination: '/maintenance' },
|
||||
'/studio/[slug]': { status: 307, destination: '/maintenance' },
|
||||
'/studio/list': { status: 307, destination: '/maintenance' },
|
||||
'/request': { status: 307, destination: '/maintenance' }
|
||||
},
|
||||
security: {
|
||||
|
|
|
|||
|
|
@ -62,7 +62,7 @@ const { session } = Astro.locals
|
|||
<DropdownItem href='/game/list'>{m.gamelist()}</DropdownItem>
|
||||
<DropdownItem href='/series/list'>{m.series()}</DropdownItem>
|
||||
<DropdownItem href='/publisher/list'>{m.publishers()}</DropdownItem>
|
||||
<DropdownItem href='/platforms/list'>{m.platforms()}</DropdownItem>
|
||||
<DropdownItem href='/platform/list'>{m.platforms()}</DropdownItem>
|
||||
</Fragment>
|
||||
</Dropdown>
|
||||
|
||||
|
|
|
|||
|
|
@ -1,10 +0,0 @@
|
|||
const locale =
|
||||
navigator && navigator.languages && navigator.languages.length ? navigator.languages[0] : navigator.language
|
||||
|
||||
interface Props {
|
||||
releaseDate: Date
|
||||
}
|
||||
|
||||
export default function ReleaseDate(props: Props) {
|
||||
return <td>{new Intl.DateTimeFormat(locale, { dateStyle: 'medium' }).format(props.releaseDate)}</td>
|
||||
}
|
||||
21
src/components/letterList/PlatformList.astro
Normal file
21
src/components/letterList/PlatformList.astro
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
---
|
||||
import prismaClient from 'utils/prisma-client'
|
||||
|
||||
interface Props {
|
||||
letter: string
|
||||
}
|
||||
|
||||
const { letter } = Astro.props
|
||||
const games = await prismaClient.platform.findMany({
|
||||
where: { name: { startsWith: letter } },
|
||||
select: { id: true, name: true }
|
||||
})
|
||||
---
|
||||
|
||||
{
|
||||
games.map((a) => (
|
||||
<a class='text-left hover:bg-btn-gray/30 rounded-md p-2' href={`/platform/${a.id}`}>
|
||||
{a.name}
|
||||
</a>
|
||||
))
|
||||
}
|
||||
21
src/components/letterList/PublisherList.astro
Normal file
21
src/components/letterList/PublisherList.astro
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
---
|
||||
import prismaClient from 'utils/prisma-client'
|
||||
|
||||
interface Props {
|
||||
letter: string
|
||||
}
|
||||
|
||||
const { letter } = Astro.props
|
||||
const games = await prismaClient.publisher.findMany({
|
||||
where: { name: { startsWith: letter } },
|
||||
select: { id: true, name: true }
|
||||
})
|
||||
---
|
||||
|
||||
{
|
||||
games.map((a) => (
|
||||
<a class='text-left hover:bg-btn-gray/30 rounded-md p-2' href={`/publisher/${a.id}`}>
|
||||
{a.name}
|
||||
</a>
|
||||
))
|
||||
}
|
||||
|
|
@ -15,7 +15,7 @@ const albums = await prismaClient.series.findMany({
|
|||
|
||||
{
|
||||
albums.map((a) => (
|
||||
<a class='text-left hover:bg-btn-gray/30 rounded-md p-2' href={`/album/${a.slug}`}>
|
||||
<a class='text-left hover:bg-btn-gray/30 rounded-md p-2' href={`/series/${a.slug}`}>
|
||||
{a.name}
|
||||
</a>
|
||||
))
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ const albums = await prismaClient.series.findMany({
|
|||
|
||||
{
|
||||
albums.map((a) => (
|
||||
<a class='group text-left rounded-md p-2' href={`/album/${a.slug}`}>
|
||||
<a class='group text-left rounded-md p-2' href={`/series/${a.slug}`}>
|
||||
<Image
|
||||
src={`https://cdn.sittingonclouds.net/series/${a.slug}.png`}
|
||||
alt={a.slug}
|
||||
|
|
|
|||
21
src/components/letterList/StudioList.astro
Normal file
21
src/components/letterList/StudioList.astro
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
---
|
||||
import prismaClient from 'utils/prisma-client'
|
||||
|
||||
interface Props {
|
||||
letter: string
|
||||
}
|
||||
|
||||
const { letter } = Astro.props
|
||||
const games = await prismaClient.studio.findMany({
|
||||
where: { name: { startsWith: letter } },
|
||||
select: { slug: true, name: true }
|
||||
})
|
||||
---
|
||||
|
||||
{
|
||||
games.map((a) => (
|
||||
<a class='text-left hover:bg-btn-gray/30 rounded-md p-2' href={`/studio/${a.slug}`}>
|
||||
{a.name}
|
||||
</a>
|
||||
))
|
||||
}
|
||||
|
|
@ -9,6 +9,7 @@ import BaseLayout from 'layouts/base.astro'
|
|||
import TrackList from 'components/albumPage/TrackList'
|
||||
import DownloadBtn from 'components/albumPage/DownloadBtn.astro'
|
||||
import AlbumBox from 'components/AlbumBox.astro'
|
||||
import releaseDate from 'utils/releaseDate'
|
||||
|
||||
import kofi from 'img/socials/ko-fi-donate-button.png'
|
||||
import discord from 'img/socials/discord.png'
|
||||
|
|
@ -130,7 +131,7 @@ const coverImage = await getImage({
|
|||
<tr>
|
||||
<th class='width-row'>{m.releaseDate()}</th>
|
||||
<td>
|
||||
{new Intl.DateTimeFormat(currentLocale, { dateStyle: 'medium' }).format(album?.releaseDate)}
|
||||
{releaseDate(album?.releaseDate)}
|
||||
</td>
|
||||
</tr>
|
||||
) : null
|
||||
|
|
|
|||
126
src/pages/anim/[id].astro
Normal file
126
src/pages/anim/[id].astro
Normal file
|
|
@ -0,0 +1,126 @@
|
|||
---
|
||||
import { SEO } from 'astro-seo'
|
||||
import * as m from 'paraglide/messages'
|
||||
import prismaClient from 'utils/prisma-client'
|
||||
import { getImage } from 'astro:assets'
|
||||
import { Image } from 'astro:assets'
|
||||
|
||||
import BaseLayout from 'layouts/base.astro'
|
||||
import AlbumBox from 'components/AlbumBox.astro'
|
||||
import releaseDate from 'utils/releaseDate'
|
||||
|
||||
const { id } = Astro.params
|
||||
|
||||
if (!id) return Astro.redirect('/404')
|
||||
const animId = parseInt(id)
|
||||
if (!animId) return Astro.redirect('/404')
|
||||
|
||||
const anim = await prismaClient.animation.findUnique({
|
||||
where: { id: animId },
|
||||
select: {
|
||||
title: true,
|
||||
subTitle: true,
|
||||
releaseDate: true,
|
||||
headerColor: true,
|
||||
studios: { select: { studioSlug: true, studio: { select: { name: true } } } },
|
||||
albums: { select: { album: { select: { title: true, id: true } } } }
|
||||
}
|
||||
})
|
||||
|
||||
if (!anim) return Astro.redirect('/404')
|
||||
|
||||
const coverImage = await getImage({
|
||||
src: `https://cdn.sittingonclouds.net/anim/${animId}.png`,
|
||||
height: 150,
|
||||
width: 150
|
||||
})
|
||||
const { currentLocale } = Astro
|
||||
---
|
||||
|
||||
<SEO
|
||||
slot='head'
|
||||
titleDefault='Sitting on Clouds'
|
||||
title={anim.title ?? undefined}
|
||||
description='Largest Video Game & Animation Soundtrack サウンドトラック Archive'
|
||||
openGraph={{
|
||||
basic: {
|
||||
title: anim.title ?? '',
|
||||
type: 'website',
|
||||
image: `https://www.sittingonclouds.net${coverImage.src}`,
|
||||
url: Astro.url.pathname
|
||||
},
|
||||
optional: {
|
||||
description:
|
||||
anim.subTitle && anim.studios.length > 0
|
||||
? `${anim.subTitle} / ${anim.studios.map((a) => a.studio.name).join(' - ')}`
|
||||
: anim.subTitle || anim.studios.map((a) => a.studio.name).join(' - '),
|
||||
siteName: 'Sitting on Clouds'
|
||||
}
|
||||
}}
|
||||
extend={{
|
||||
meta: [{ name: 'theme-color', content: anim.headerColor ?? '#ffffff' }]
|
||||
}}
|
||||
/>
|
||||
|
||||
<BaseLayout>
|
||||
<div
|
||||
class={`w-full min-h-100vh bg-fixed bg-center bg-cover`}
|
||||
style={`background-image: url('https://cdn.sittingonclouds.net/anim/${animId}.png');`}
|
||||
>
|
||||
<div class='bg-gray/70 w-full min-h-full'>
|
||||
<div class='flex flex-col md:flex-row gap-x-4 max-w-[1000px] mx-auto p-3 min-h-full'>
|
||||
<div class='flex flex-col gap-y-2 w-full md:w-[430px] bg-dark rounded-md size-full py-2 px-3'>
|
||||
<div>
|
||||
<Image
|
||||
src={`https://cdn.sittingonclouds.net/anim/${animId}.png`}
|
||||
alt={`${anim.title} cover`}
|
||||
class='rounded-sm size-full object-contain h-fit'
|
||||
quality='mid'
|
||||
width={500}
|
||||
height={500}
|
||||
/>
|
||||
</div>
|
||||
<div class='font-medium text-3xl text-center'>{anim.title}</div>
|
||||
{anim.subTitle ? <div class='font-medium text-xl text-center'>{anim.subTitle}</div> : null}
|
||||
<div class='table'>
|
||||
{
|
||||
anim.releaseDate ? (
|
||||
<div class='table-row'>
|
||||
<div class='table-cell text-md font-medium'>{m.releaseDate()}</div>
|
||||
<div class='table-cell text-md'>{releaseDate(anim.releaseDate)}</div>
|
||||
</div>
|
||||
) : null
|
||||
}
|
||||
{
|
||||
anim.studios.length > 0 ? (
|
||||
<>
|
||||
<div class='table-row'>
|
||||
<div class='table-cell text-md font-medium'>{m.studios()}</div>
|
||||
<div class='table-cell text-md'>
|
||||
<a href={`/studio/${anim.studios[0].studioSlug}`}>{anim.studios[0].studio.name}</a>
|
||||
</div>
|
||||
</div>
|
||||
{anim.studios.slice(1).map((a) => (
|
||||
<div class='table-row'>
|
||||
<div class='table-cell text-md font-medium' />
|
||||
<div class='table-cell text-md'>
|
||||
<a href={`/studio/${a.studioSlug}`}>{a.studio.name}</a>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</>
|
||||
) : null
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
<div class='grid w-full grid-cols-2 md:grid-cols-4 gap-x-2.5'>
|
||||
{
|
||||
anim.albums.map((a) => (
|
||||
<AlbumBox href={`/album/${a.album.id}`} image={`/album/${a.album.id}.png`} title={a.album.title} />
|
||||
))
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</BaseLayout>
|
||||
146
src/pages/game/[slug].astro
Normal file
146
src/pages/game/[slug].astro
Normal file
|
|
@ -0,0 +1,146 @@
|
|||
---
|
||||
import prismaClient from 'utils/prisma-client'
|
||||
import * as m from 'paraglide/messages'
|
||||
import { getImage, Image } from 'astro:assets'
|
||||
import { SEO } from 'astro-seo'
|
||||
|
||||
import BaseLayout from 'layouts/base.astro'
|
||||
|
||||
import AlbumBox from 'components/AlbumBox.astro'
|
||||
import releaseDate from 'utils/releaseDate'
|
||||
|
||||
const { slug } = Astro.params
|
||||
if (!slug) return Astro.redirect('/404')
|
||||
|
||||
const game = await prismaClient.game.findUnique({
|
||||
where: { slug },
|
||||
select: {
|
||||
name: true,
|
||||
releaseDate: true,
|
||||
publishers: { select: { publisher: { select: { name: true, id: true } } } },
|
||||
platforms: { select: { platform: { select: { name: true, id: true } } } },
|
||||
series: { select: { series: { select: { name: true, slug: true } } } },
|
||||
headerColor: true,
|
||||
albums: {
|
||||
select: { game: true, album: { select: { games: true, id: true, title: true, publishedAt: true } } },
|
||||
orderBy: { album: { publishedAt: 'desc' } }
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
if (!game) return Astro.redirect('/404')
|
||||
|
||||
const { currentLocale } = Astro
|
||||
const coverImage = await getImage({
|
||||
src: `https://cdn.sittingonclouds.net/game/${slug}.png`,
|
||||
height: 150,
|
||||
width: 150
|
||||
})
|
||||
---
|
||||
|
||||
<SEO
|
||||
slot='head'
|
||||
titleDefault='Sitting on Clouds'
|
||||
title={game.name ?? undefined}
|
||||
description='Largest Video Game & Animation Soundtrack サウンドトラック Archive'
|
||||
openGraph={{
|
||||
basic: {
|
||||
title: game.name ?? '',
|
||||
type: 'website',
|
||||
image: `https://www.sittingonclouds.net${coverImage.src}`,
|
||||
url: Astro.url.pathname
|
||||
},
|
||||
optional: {
|
||||
description: `${game.series.map((s) => s.series.name).join(' - ')}${game.series.length > 0 ? ' / ' : ''}${game.publishers.map((p) => p.publisher.name).join(' - ')}`,
|
||||
siteName: 'Sitting on Clouds'
|
||||
}
|
||||
}}
|
||||
extend={{
|
||||
meta: [{ name: 'theme-color', content: game.headerColor ?? '#121212' }]
|
||||
}}
|
||||
/>
|
||||
|
||||
<BaseLayout>
|
||||
<div class='flex flex-col flex-1 max-w-[1440px] mx-auto p-3'>
|
||||
<div class='flex flex-wrap md:flex-nowrap gap-x-3'>
|
||||
<div class='flex-1 flex-wrap md:flex-5/12'>
|
||||
<div class='size-full relative cursor-pointer'>
|
||||
<!-- <Image
|
||||
src={`https://cdn.sittingonclouds.net/game/${slug}.png`}
|
||||
alt={`${game.name} logo`}
|
||||
class='rounded-sm size-full object-contain h-fit mb-2 md:min-h-96 px-10'
|
||||
quality='mid'
|
||||
width={500}
|
||||
height={500}
|
||||
/> -->
|
||||
</div>
|
||||
</div>
|
||||
<div class='flex-1 flex-wrap md:flex-7/12'>
|
||||
<div class='bg-dark rounded-xl py-4 px-6'>
|
||||
<div class='font-medium text-4xl text-center'>{game.name}</div>
|
||||
<table class='mt-4 border-spacing-y-4 border-separate w-full'>
|
||||
<tbody>
|
||||
{
|
||||
game.releaseDate ? (
|
||||
<tr>
|
||||
<th class='width-row'>{m.releaseDate()}:</th>
|
||||
<td>{releaseDate(game.releaseDate)}</td>
|
||||
</tr>
|
||||
) : null
|
||||
}
|
||||
<tr>
|
||||
<th class='width-row'>{m.publishers()}:</th>
|
||||
<td
|
||||
>{
|
||||
game.publishers.map((p, i) => (
|
||||
<>
|
||||
<a href={`/publisher/${p.publisher.id}`}>{p.publisher.name}</a>
|
||||
{i < game.publishers.length - 1 ? <span> - </span> : null}
|
||||
</>
|
||||
))
|
||||
}</td
|
||||
>
|
||||
</tr>
|
||||
<tr>
|
||||
<th class='width-row'>{m.platforms()}:</th>
|
||||
<td
|
||||
>{
|
||||
game.platforms.map((p, i) => (
|
||||
<>
|
||||
<a href={`/platform/${p.platform.id}`}>{p.platform.name}</a>
|
||||
{i < game.platforms.length - 1 ? <span> - </span> : null}
|
||||
</>
|
||||
))
|
||||
}</td
|
||||
>
|
||||
</tr>
|
||||
<tr>
|
||||
<th class='width-row'>{m.series()}:</th>
|
||||
<td
|
||||
>{
|
||||
game.series.map((p, i) => (
|
||||
<>
|
||||
<a href={`/series/${p.series.slug}`}>{p.series.name}</a>
|
||||
{i < game.series.length - 1 ? <span> - </span> : null}
|
||||
</>
|
||||
))
|
||||
}</td
|
||||
>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<hr />
|
||||
<div class='flex flex-wrap md:flex-nowrap gap-x-3'>
|
||||
<div class='grid grid-cols-5 gap-x-2'>
|
||||
{
|
||||
game.albums.map((a) => (
|
||||
<AlbumBox href={`/album/${a.album.id}`} image={`/album/${a.album.id}.png`} title={a.album.title} />
|
||||
))
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</BaseLayout>
|
||||
36
src/pages/platform/list/index.astro
Normal file
36
src/pages/platform/list/index.astro
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
---
|
||||
import prismaClient from 'utils/prisma-client'
|
||||
|
||||
import LetterList from 'layouts/LetterList.astro'
|
||||
import GameList from 'components/letterList/GameList.astro'
|
||||
import PublisherList from 'components/letterList/PublisherList.astro'
|
||||
import PlatformList from 'components/letterList/PlatformList.astro'
|
||||
|
||||
const letters: { letter: string; count: BigInt }[] = await prismaClient.$queryRaw`
|
||||
SELECT DISTINCT UPPER(LEFT(platform.name, 1)) AS letter, COUNT(*) AS count
|
||||
FROM Album_Platform, albums, platform
|
||||
WHERE Album_Platform.albumId = albums.id
|
||||
AND Album_Platform.platformId = platform.id
|
||||
GROUP BY letter
|
||||
ORDER BY letter;
|
||||
`
|
||||
---
|
||||
|
||||
<LetterList letters={letters}>
|
||||
{
|
||||
letters.map((l) => (
|
||||
<div id={l.letter}>
|
||||
<div class='flex uppercase border-y-2 text-4xl justify-center border-white py-1.5'>{l.letter}</div>
|
||||
<div class='py-4 grid sm:grid-cols-1 md:grid-cols-4 gap-1'>
|
||||
<PlatformList letter={l.letter} server:defer>
|
||||
<Fragment slot='fallback'>
|
||||
{Array.from({ length: Number(l.count) }).map(() => (
|
||||
<div class='animate-pulse h-6 w-full bg-gray/85' />
|
||||
))}
|
||||
</Fragment>
|
||||
</PlatformList>
|
||||
</div>
|
||||
</div>
|
||||
))
|
||||
}
|
||||
</LetterList>
|
||||
35
src/pages/publisher/list/index.astro
Normal file
35
src/pages/publisher/list/index.astro
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
---
|
||||
import prismaClient from 'utils/prisma-client'
|
||||
|
||||
import LetterList from 'layouts/LetterList.astro'
|
||||
import GameList from 'components/letterList/GameList.astro'
|
||||
import PublisherList from 'components/letterList/PublisherList.astro'
|
||||
|
||||
const letters: { letter: string; count: BigInt }[] = await prismaClient.$queryRaw`
|
||||
SELECT DISTINCT UPPER(LEFT(publisher.name, 1)) AS letter, COUNT(*) AS count
|
||||
FROM Publisher_Game, publisher, game
|
||||
WHERE Publisher_Game.publisherId = publisher.id
|
||||
AND Publisher_Game.gameSlug = game.slug
|
||||
GROUP BY letter
|
||||
ORDER BY letter;
|
||||
`
|
||||
---
|
||||
|
||||
<LetterList letters={letters}>
|
||||
{
|
||||
letters.map((l) => (
|
||||
<div id={l.letter}>
|
||||
<div class='flex uppercase border-y-2 text-4xl justify-center border-white py-1.5'>{l.letter}</div>
|
||||
<div class='py-4 grid sm:grid-cols-1 md:grid-cols-4 gap-1'>
|
||||
<PublisherList letter={l.letter} server:defer>
|
||||
<Fragment slot='fallback'>
|
||||
{Array.from({ length: Number(l.count) }).map(() => (
|
||||
<div class='animate-pulse h-6 w-full bg-gray/85' />
|
||||
))}
|
||||
</Fragment>
|
||||
</PublisherList>
|
||||
</div>
|
||||
</div>
|
||||
))
|
||||
}
|
||||
</LetterList>
|
||||
35
src/pages/studio/list/index.astro
Normal file
35
src/pages/studio/list/index.astro
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
---
|
||||
import prismaClient from 'utils/prisma-client'
|
||||
|
||||
import LetterList from 'layouts/LetterList.astro'
|
||||
import GameList from 'components/letterList/GameList.astro'
|
||||
import PublisherList from 'components/letterList/PublisherList.astro'
|
||||
|
||||
const letters: { letter: string; count: BigInt }[] = await prismaClient.$queryRaw`
|
||||
SELECT DISTINCT UPPER(LEFT(studio.name, 1)) AS letter, COUNT(*) AS count
|
||||
FROM Studio_Animation, studio, animation
|
||||
WHERE Studio_Animation.animationId = animation.id
|
||||
AND Studio_Animation.studioSlug = studio.slug
|
||||
GROUP BY letter
|
||||
ORDER BY letter;
|
||||
`
|
||||
---
|
||||
|
||||
<LetterList letters={letters}>
|
||||
{
|
||||
letters.map((l) => (
|
||||
<div id={l.letter}>
|
||||
<div class='flex uppercase border-y-2 text-4xl justify-center border-white py-1.5'>{l.letter}</div>
|
||||
<div class='py-4 grid sm:grid-cols-1 md:grid-cols-4 gap-1'>
|
||||
<PublisherList letter={l.letter} server:defer>
|
||||
<Fragment slot='fallback'>
|
||||
{Array.from({ length: Number(l.count) }).map(() => (
|
||||
<div class='animate-pulse h-6 w-full bg-gray/85' />
|
||||
))}
|
||||
</Fragment>
|
||||
</PublisherList>
|
||||
</div>
|
||||
</div>
|
||||
))
|
||||
}
|
||||
</LetterList>
|
||||
6
src/utils/releaseDate.ts
Normal file
6
src/utils/releaseDate.ts
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
const locale =
|
||||
navigator && navigator.languages && navigator.languages.length ? navigator.languages[0] : navigator.language
|
||||
|
||||
const releaseDate = (date: Date) => new Intl.DateTimeFormat(locale, { dateStyle: 'medium' }).format(date)
|
||||
|
||||
export default releaseDate
|
||||
Loading…
Add table
Add a link
Reference in a new issue