Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: ニュース一覧・詳細ページを追加 #32

Merged
merged 7 commits into from
Jul 3, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .env.sample
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
BACKEND_URL=https://graphql-engine.asis-be.orb.local/v1/graphql
1 change: 1 addition & 0 deletions .github/workflows/cd.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ jobs:
branch: 'main'
gh-app-id: ${{ vars.BOT_APP_ID }}
pr-comment-enabled: false
backend-url: ${{ vars.STG_BACKEND_URL }}
secrets:
cloudflare-account-id: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
cloudflare-api-token: ${{ secrets.CLOUDFLARE_API_TOKEN }}
Expand Down
1 change: 1 addition & 0 deletions .github/workflows/preview.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ jobs:
branch: 'preview'
gh-app-id: ${{ vars.BOT_APP_ID }}
pr-comment-enabled: true
backend-url: ${{ vars.DEV_BACKEND_URL }}
secrets:
cloudflare-account-id: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
cloudflare-api-token: ${{ secrets.CLOUDFLARE_API_TOKEN }}
Expand Down
1 change: 1 addition & 0 deletions .github/workflows/production.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ jobs:
branch: 'main'
gh-app-id: ${{ vars.BOT_APP_ID }}
pr-comment-enabled: false
backend-url: ${{ vars.PROD_BACKEND_URL }}
secrets:
cloudflare-account-id: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
cloudflare-api-token: ${{ secrets.CLOUDFLARE_API_TOKEN }}
Expand Down
6 changes: 6 additions & 0 deletions .github/workflows/wc-deploy-cloudflare-pages.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@ on:
pr-comment-enabled:
required: true
type: boolean
backend-url:
required: true
type: string
secrets:
gh-app-private-key:
required: true
Expand Down Expand Up @@ -43,6 +46,9 @@ jobs:
- name: Install deps
run: bun install --frozen-lockfile

- name: Generate env
run: bun plop env -- --backendUrl "${{ inputs.backend-url }}"

- name: Build
run: bun run build

Expand Down
8 changes: 5 additions & 3 deletions astro.config.mjs
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import { defineConfig } from 'astro/config'

import cloudflare from '@astrojs/cloudflare'
import sitemap from '@astrojs/sitemap'
import { defineConfig } from 'astro/config'

// https://astro.build/config
export default defineConfig({
site: 'https://asis.quest',
integrations: [sitemap()]
integrations: [sitemap()],
output: 'server',
adapter: cloudflare()
})
Binary file modified bun.lockb
Binary file not shown.
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,9 @@
"plop": "plop"
},
"dependencies": {
"@astrojs/cloudflare": "11.0.1",
"@astrojs/sitemap": "3.1.6",
"astro": "4.11.3",
"sharp": "0.33.4"
"astro": "4.11.3"
},
"devDependencies": {
"@astrojs/check": "0.7.0",
Expand Down
39 changes: 30 additions & 9 deletions plopfile.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,40 @@
export default function (plop) {
plop.setGenerator('sample', {
description: 'sample',
prompts: [{
type: 'input',
name: 'name',
message: 'sample name please'
}],
prompts: [
{
type: 'input',
name: 'name',
message: 'sample name please'
}
],
actions: [
{
type: 'addMany',
templateFiles: 'templates/sample/**',
destination: './sample/{{name}}',
base: 'templates/sample',
abortOnFail: true,
},
abortOnFail: true
}
]
})

plop.setGenerator('env', {
description: 'env',
prompts: [
{
type: 'input',
name: 'backendUrl',
message: 'backend url please'
}
],
actions: [
{
type: 'add',
templateFile: 'templates/env/.env.hbs',
path: '.env',
force: true
}
]
});
};
})
}
23 changes: 23 additions & 0 deletions src/components/For.astro
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
---
import Show from './Show.astro';

interface Props<T> {
each: Iterable<T>;
}

const { each } = Astro.props;
---

{
(async function* () {
for await (const value of each) {
let html = await Astro.slots.render('default', [value]);
yield <Fragment set:html={html} />;
yield '\n';
}
})()
}

<Show when={!each.length}>
<slot name="fallback" />
</Show>
9 changes: 9 additions & 0 deletions src/components/Show.astro
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
---
interface Props<T> {
when: T | number | boolean | undefined | null;
}

const { when } = Astro.props;
---

{!!when ? <slot /> : <slot name="fallback" />}
88 changes: 88 additions & 0 deletions src/lib/api.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
import type { News } from '@/types.ts'

/**
* Fetches the GraphQL API
* @param operationsDoc
* @param operationName
* @param variables
*/
async function fetchGraphQL(
operationsDoc: string,
operationName: string,
// biome-ignore lint/suspicious/noExplicitAny: <explanation>
variables: Record<string, any>
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Improve the type of variables parameter.

Instead of using Record<string, any>, consider defining a more specific type for the variables parameter to improve type safety.

-  variables: Record<string, any>
+  variables: Record<string, unknown>
Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
variables: Record<string, any>
variables: Record<string, unknown>

) {
const result = await fetch(`${import.meta.env.BACKEND_URL}`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
query: operationsDoc,
variables,
operationName
})
})
return await result.json()
}

/**
* Fetches the list of news
*/
export async function fetchNewsList(): Promise<News[]> {
const { errors, data } = await fetchGraphQL(
`
query GetNewsList {
news {
title
slug
publishedAt
excerpt
coverImageUrl
content
}
}
`,
'GetNewsList',
{}
)

if (errors) {
// handle those errors like a pro
console.error(errors)
}

console.log(data)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Replace console logs with a proper logging mechanism.

Using console.log is not recommended for production code. Consider using a logging library to handle logs.

-  console.log(data)
+  // Use a logging library instead of console.log
+  logger.info('Fetched news list', data)

Committable suggestion was skipped due to low confidence.

return data.news as News[]
}
Comment on lines +30 to +55
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add proper error handling.

The function should handle errors more gracefully rather than just logging them to the console.

  if (errors) {
    // handle those errors like a pro
    console.error(errors)
    throw new Error('Failed to fetch news list')
  }
Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
export async function fetchNewsList(): Promise<News[]> {
const { errors, data } = await fetchGraphQL(
`
query GetNewsList {
news {
title
slug
publishedAt
excerpt
coverImageUrl
content
}
}
`,
'GetNewsList',
{}
)
if (errors) {
// handle those errors like a pro
console.error(errors)
}
console.log(data)
return data.news as News[]
}
export async function fetchNewsList(): Promise<News[]> {
const { errors, data } = await fetchGraphQL(
`
query GetNewsList {
news {
title
slug
publishedAt
excerpt
coverImageUrl
content
}
}
`,
'GetNewsList',
{}
)
if (errors) {
// handle those errors like a pro
console.error(errors)
throw new Error('Failed to fetch news list')
}
console.log(data)
return data.news as News[]
}


/**
* Fetches the list of news
*/
export async function fetchNewsBySlug(slug: string): Promise<News> {
const { errors, data } = await fetchGraphQL(
`
query GetNewsBySlug($slug: String) {
news(where: {slug: {_eq: $slug}}) {
title
slug
publishedAt
excerpt
coverImageUrl
content
}
}
`,
'GetNewsBySlug',
{
slug
}
)

if (errors) {
// handle those errors like a pro
console.error(errors)
}

console.log(data)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Replace console logs with a proper logging mechanism.

Using console.log is not recommended for production code. Consider using a logging library to handle logs.

-  console.log(data)
+  // Use a logging library instead of console.log
+  logger.info('Fetched news by slug', data)

Committable suggestion was skipped due to low confidence.

// biome-ignore lint/style/noNonNullAssertion: <explanation>
return (data.news as News[])[0]!
Comment on lines +60 to +87
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add proper error handling.

The function should handle errors more gracefully rather than just logging them to the console.

  if (errors) {
    // handle those errors like a pro
    console.error(errors)
    throw new Error('Failed to fetch news by slug')
  }
Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
export async function fetchNewsBySlug(slug: string): Promise<News> {
const { errors, data } = await fetchGraphQL(
`
query GetNewsBySlug($slug: String) {
news(where: {slug: {_eq: $slug}}) {
title
slug
publishedAt
excerpt
coverImageUrl
content
}
}
`,
'GetNewsBySlug',
{
slug
}
)
if (errors) {
// handle those errors like a pro
console.error(errors)
}
console.log(data)
// biome-ignore lint/style/noNonNullAssertion: <explanation>
return (data.news as News[])[0]!
if (errors) {
// handle those errors like a pro
console.error(errors)
throw new Error('Failed to fetch news by slug')
}

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Avoid non-null assertion.

Instead of using non-null assertion, handle the null case properly to avoid potential runtime errors.

-  return (data.news as News[])[0]!
+  if (!data.news || data.news.length === 0) {
+    throw new Error('No news found for the given slug')
+  }
+  return data.news[0] as News
Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
return (data.news as News[])[0]!
if (!data.news || data.news.length === 0) {
throw new Error('No news found for the given slug')
}
return data.news[0] as News

}
2 changes: 2 additions & 0 deletions src/pages/404.astro
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
---
import Hero from '../components/Hero.astro'
import BaseLayout from '../layouts/BaseLayout.astro'

export const prerender = true
---

<BaseLayout title="Not Found" description="404 Error — this page was not found">
Expand Down
2 changes: 2 additions & 0 deletions src/pages/index.astro
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import Hero from '../components/Hero.astro'

// Full Astro Component Syntax:
// https://docs.astro.build/core-concepts/astro-components/

export const prerender = true
---

<BaseLayout>
Expand Down
16 changes: 16 additions & 0 deletions src/pages/news/[slug].astro
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
---
import BaseLayout from '@/layouts/BaseLayout.astro'
import { fetchNewsBySlug } from '../../lib/api'

const { slug } = Astro.params as { slug: string }

const news = await fetchNewsBySlug(slug)
---

<BaseLayout>
<div class="stack gap-20 lg:gap-48">
<h2>{news.title}</h2>
<p>{news.publishedAt}</p>
<div set:html={news.content} />
</div>
</BaseLayout>
22 changes: 22 additions & 0 deletions src/pages/news/index.astro
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
---
import For from '@/components/For.astro'
import BaseLayout from '@/layouts/BaseLayout.astro'
import { fetchNewsList } from '@/lib/api'
import type { News } from '@/types'

const newsList = await fetchNewsList()
---

<BaseLayout>
<ul class="stack gap-20 lg:gap-48">
<For each={newsList}>{(news: News) => (
<li>
<a href=`news/${news.slug}`>
<h2>{news.title}</h2>
<p>{news.publishedAt}</p>
<p>{news.excerpt}</p>
</a>
</li>
)}</For>
</ul>
</BaseLayout>
8 changes: 8 additions & 0 deletions src/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
export interface News {
title: string
slug: string
publishedAt: number
excerpt: string
coverImageUrl: string
content: string
}
1 change: 1 addition & 0 deletions templates/env/.env.hbs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
BACKEND_URL={{backendUrl}}
Loading