Open6
tensura-app
ピン留めされたアイテム
今後やること
- データベースとの連携
- データの整理
- 各用語(詳細画面)に固有のURLも割り当てる
- 検索機能
- フィルター機能
- 認証機能
- フッターの整形
- ヒーローセクション
- ロゴ(ホーム)ボタン
- ハンバーガーメニューをshadcnのsheetを遣う(久々に30:00くらい)
- [id]フォルダの作り方(久々に35:00くらい)
- カードをホバーした時に影を強くする(transition、durationも忘れずに 久々に1:00:00くらい)
- カードコンポーネントの作り方(型との紐付け、1:00:00くらい)
GitHub Pages サイトを作成できない
Buildでエラーが出る。
エラー内容
Run npx --no-install next build
npx --no-install next build
shell: /usr/bin/bash -e {0}
env:
GITHUB_PAGES: true
⚠ No build cache found. Please configure build caching for faster rebuilds. Read more: https://nextjs.org/docs/messages/no-cache
Attention: Next.js now collects completely anonymous telemetry regarding usage.
This information is used to shape Next.js' roadmap and prioritize features.
You can learn more, including how to opt-out if you'd not like to participate in this anonymous program, by visiting the following URL:
https://nextjs.org/telemetry
▲ Next.js 14.2.5
Creating an optimized production build ...
Failed to compile.
./utils/supabase/server.ts
Error:
x You're importing a component that needs next/headers. That only works in a Server Component which is not supported in the pages/ directory. Read more: https://nextjs.org/docs/getting-started/
| react-essentials#server-components
|
|
,-[/home/runner/work/tensura-app/tensura-app/utils/supabase/server.ts:1:1]
1 | import { createServerClient } from "@supabase/ssr";
2 | import { cookies } from "next/headers";
: ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
3 |
4 | export const createClient = () => {
5 | const cookieStore = cookies();
`----
Import trace for requested module:
./utils/supabase/server.ts
./components/itemList.tsx
./app/layout.tsx
Module not found: Can't resolve './clientLayout'
https://nextjs.org/docs/messages/module-not-found
> Build failed because of webpack errors
Error: Process completed with exit code 1.
GitHub Pagesの作り方
どうやらSupabaseのserver/clientファイルがおかしい模様。
server/clientを削除している例を見て試してみる。
CardSectionにSupabaseのデータが反映されない
hook/useCardSection.ts
import { useState, useEffect } from "react";
import { createClient } from "@/utils/supabase/client";
interface ItemData {
name: string;
summary: string;
category: string;
}
export const useCardSections = (categories: string[]) => {
const [sections, setSections] = useState<{ [key: string]: ItemData[] }>({});
const supabase = createClient();
useEffect(() => {
const fetchItems = async () => {
const { data, error } = await supabase
.from("items")
.select("name, summary, category")
.or("category.eq.character,category.eq.skills");
if (error) {
console.error("Error fetching items:", error);
return;
}
console.log("Fetched data:", data); // データ取得の確認
const groupedItems = data.reduce((acc, item) => {
if (!acc[item.category]) {
acc[item.category] = [];
}
acc[item.category].push(item);
return acc;
}, {} as { [key: string]: ItemData[] });
console.log("Grouped items:", groupedItems); // グループ化されたデータの確認
setSections(groupedItems);
};
fetchItems();
}, []);
return sections;
};
components/CardSection.tsx
import React from "react";
import { Card, CardContent } from "@/components/ui/card";
import {
Carousel,
CarouselContent,
CarouselItem,
} from "@/components/ui/carousel";
import { cn } from "@/lib/utils";
import { useCardSections } from "../hook/useCardSections";
interface SectionConfig {
category: string;
title: string;
allLink: string;
color: string;
}
const cardSectionConfigs: SectionConfig[] = [
{
category: "character",
title: "Characters",
allLink: "/all-characters",
color: "bg-blue-500",
},
{
category: "skills",
title: "Skills",
allLink: "/all-skills",
color: "bg-red-500",
},
];
const CardSection: React.FC = () => {
const sections = useCardSections(
cardSectionConfigs.map((config) => config.category)
);
return (
<div className="flex-grow p-4">
{cardSectionConfigs.map((config, sectionIndex) => {
const sectionItems = sections[config.category] || [];
return (
<section key={sectionIndex} className="my-6">
<div className="flex justify-between items-center mb-3 px-3">
<h2 className="text-xl font-bold">{config.title}</h2>
<a href={config.allLink} className="text-sm">
All {config.title}
</a>
</div>
<Carousel
opts={{
align: "start",
}}
className="w-full"
>
<CarouselContent className="w-[93%] ml-[2px]">
{sectionItems
.reduce<any[][]>((result, item, index) => {
if (index % 2 === 0) {
result.push([item]);
} else {
result[result.length - 1].push(item);
}
return result;
}, [])
.map((pair, pairIndex) => (
<CarouselItem key={pairIndex} className="basis-3/5 pl-2">
<div className="p-1">
{pair.map((item, itemIndex) => (
<Card key={itemIndex} className="mb-4">
<CardContent className="flex flex-col p-4 min-h-32">
<div className="flex items-center mb-2">
<div
className={cn(
"w-1 h-[18px] mr-2 inline-block",
config.color
)}
></div>
<h3 className="text-lg font-semibold">
{item.name}
</h3>
</div>
<p className="text-sm text-muted-foreground line-clamp-2">
{item.summary}
</p>
</CardContent>
</Card>
))}
</div>
</CarouselItem>
))}
</CarouselContent>
</Carousel>
</section>
);
})}
</div>
);
};
export default CardSection;