Skip to content

Commit

Permalink
Add basic informations page
Browse files Browse the repository at this point in the history
  • Loading branch information
smrtrfszm committed Mar 26, 2024
1 parent 350ddd9 commit c1e7ae9
Show file tree
Hide file tree
Showing 7 changed files with 261 additions and 0 deletions.
20 changes: 20 additions & 0 deletions src/app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import { loadHomePage } from './data/home.data'
import { loadPage } from './data/pages.data'
import { loadPost } from './data/post.data'
import { loadSearchPage } from './data/search.data'
import { loadInformationMenu } from './data/information.data'

const ROUTES: RouteDefinition[] = [
{
Expand Down Expand Up @@ -50,6 +51,25 @@ const ROUTES: RouteDefinition[] = [
path: '/canteen',
component: lazy(() => import('~/pages/Canteen')),
},
{
path: '/information',
children: [
{
path: '/',
component: lazy(() => import('~/pages/Information')),
},
{
path: '/:slug',
component: lazy(() =>
import('~/pages/Information').then((c) => ({
default: c.InformationPageWithSlug,
}))
),
load: loadPage,
},
],
load: loadInformationMenu,
},
{
path: '/_debug',
component: lazy(() => import('~/pages/Debug')),
Expand Down
68 changes: 68 additions & 0 deletions src/components/InformationMenu.module.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
.menu {
.dropdown-content {
display: flex;
flex-direction: column;
background: var(--information-menu-background);
border-radius: 0.8rem;
gap: 2rem;
}

ul, li {
list-style: none;
}

a {
display: block;
padding: 1rem 2rem;
border-radius: 0.3rem;
cursor: pointer;
transition: background 200ms;
color: inherit;
text-decoration: none;

&.active,
&:hover {
background: var(--information-menu-hover-background);
}

&.active {
color: var(--information-menu-active-color);
}
}

.section {
display: grid;
margin-top: 1rem;
grid-gap: 0.1rem;

@include md {
grid-template-columns: repeat(2, 1fr);
}

@include lg {
grid-template-columns: 1fr;
}

.section-title {
display: block;
color: var(--information-menu-section-title-color);
font-size: 1.4rem;
font-weight: 700;
padding: {
left: 2rem;
bottom: 1rem;
}
margin-bottom: 0.4rem;
border-bottom: 1px solid var(--information-menu-section-title-border);

@include md {
grid-column: span 2;
}

@include lg {
grid-column: span 1;
}
}
}
}

66 changes: 66 additions & 0 deletions src/components/InformationMenu.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import { For, Show, VoidComponent } from 'solid-js'
import { InternalOrExternalLinkMenuItem, MenuItem, PageLinkMenuItem } from '~/models/information'
import styles from './InformationMenu.module.scss'
import { A } from '@solidjs/router'

export type InformationMenuProps = {
menuItems: MenuItem[]
}

export const InformationMenu: VoidComponent<InformationMenuProps> = (props) => {
return (
<aside class={styles.menu}>
<ul class={styles.dropdownContent} role="navigation">
<For each={props.menuItems}>
{(item) => (
<li>
<InformationMenuSection item={item} />
</li>
)}
</For>
</ul>
</aside>
)
}

type InformationMenuSectionProps = {
item: MenuItem
}

const InformationMenuSection: VoidComponent<InformationMenuSectionProps> = (props) => {
return (
<ul class={styles.section}>
<span class={styles.sectionTitle}>{props.item.name}</span>
<For each={props.item.children}>
{(child) => (
<li>
<Show when={child.type === 'page_link'}>
<PageLink child={child as PageLinkMenuItem} />
</Show>
<Show when={child.type !== 'page_link'}>
<InternalOrExternalLink child={child as InternalOrExternalLinkMenuItem} />
</Show>
</li>
)}
</For>
</ul>
)
}

const PageLink: VoidComponent<{ child: PageLinkMenuItem }> = (props) => {
const link = () => `/information/${props.child.slug}`

return (
<A href={link()} activeClass={styles.active}>
{props.child.name}
</A>
)
}

const InternalOrExternalLink: VoidComponent<{ child: InternalOrExternalLinkMenuItem }> = (props) => {
return (
<A href={props.child.link}>
{props.child.name} {'->'}
</A>
)
}
40 changes: 40 additions & 0 deletions src/data/information.data.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import { gql, request } from '@solid-primitives/graphql'
import { RouteLoadFunc, cache } from '@solidjs/router'
import { GRAPHQL_BACKEND_URL } from '~/constants'
import { MenuItem } from '~/models/information'

const QUERY_INFORMATION_MENU = gql`
query InformationMenu {
menu {
name
type
link
slug
children {
name
type
link
slug
}
}
}
`

export const queryInformationMenu = cache(async (): Promise<MenuItem[]> => {
type Response = {
menu: MenuItem[]
}

const response = await request<Response>(GRAPHQL_BACKEND_URL, QUERY_INFORMATION_MENU)

if (response.menu === null) {
console.error('Failed to query information page menu')
return []
}

return response.menu
}, 'Information.queryInformationMenu')

export const loadInformationMenu: RouteLoadFunc = () => {
void queryInformationMenu()
}
16 changes: 16 additions & 0 deletions src/models/information.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
export type MenuItemCommon = {
name: string
children: MenuItem[]
}

export type PageLinkMenuItem = {
type: 'page_link'
slug: string
} & MenuItemCommon

export type InternalOrExternalLinkMenuItem = {
type: 'internal_link' | 'external_link'
link: string
} & MenuItemCommon

export type MenuItem = PageLinkMenuItem | InternalOrExternalLinkMenuItem
11 changes: 11 additions & 0 deletions src/pages/Information.module.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
.container {
width: min(var(--page-width), 100% - 2 * var(--page-padding));
margin-inline: auto;
display: grid;
grid-gap: 2rem;
margin-top: 3rem;

@include lg {
grid-template-columns: 25rem minmax(0, 1fr);
}
}
40 changes: 40 additions & 0 deletions src/pages/Information.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import { Navigate, RouteSectionProps, createAsync } from '@solidjs/router'
import { Component, Show } from 'solid-js'
import { queryInformationMenu } from '~/data/information.data'
import { queryPageByID } from '~/data/pages.data'
import { MenuItem } from '~/models/information'
import styles from './Information.module.scss'
import PageRenderer from '~/components/PageRenderer'
import { InformationMenu } from '~/components/InformationMenu'

const InformationPage: Component<RouteSectionProps> = (props) => {
const data = createAsync(queryInformationMenu)

const href = (data: MenuItem[]) => {
for (const item of data) {
if (item.type == 'page_link') {
return `${props.location.pathname}/${encodeURIComponent(item.slug)}`
}
}

console.error('No `page_link` type item in menu')

return 'error'
}

return <Show when={data()}>{(data) => <Navigate href={href(data())} />}</Show>
}

export default InformationPage

export const InformationPageWithSlug: Component<RouteSectionProps> = (props) => {
const menu = createAsync(queryInformationMenu)
const page = createAsync(() => queryPageByID(props.params.slug))

return (
<div class={styles.container}>
<Show when={menu()}>{(menu) => <InformationMenu menuItems={menu()} />}</Show>
<Show when={page()}>{(page) => <PageRenderer title={page().title} content={page().content} />}</Show>
</div>
)
}

0 comments on commit c1e7ae9

Please sign in to comment.