Replit Agent 触ってみた3
今回は気ままに機能を追加していくぞ
New Chat を始める
いくつか機能を提示されている。よし全マシでいこう!(アホ)
一旦 checkpoint を記録した上でプランを考えてくれる
Based on the BlogGuestBook application files and structure, I’ll suggest some valuable features that could enhance user engagement and functionality:
提案された機能は以下の通り:
- Real-time Social Features:
- Emoji reactions to entries (like, love, laugh, etc.)
- Reply threading to enable conversations
- @mentions to tag other users
- Rich text formatting with markdown support
- User Experience Improvements:
- User profiles with customizable avatars
- Entry categorization with tags
- Search functionality with filters
- Dark/light theme toggle
- Content Management:
- Image attachment support
- Entry moderation tools
- Spam protection
- Entry drafts and scheduling
- Analytics and Engagement:
- View counts for entries
- Popular entries showcase
- User activity statistics
- Email notifications for replies
Would you like me to help implement any of these features to enhance the BlogGuestBook application?
必要な機能が過不足なく揃っているか、それが期待通りに動くか、などを確認しないといけないがどうすればいいだろうか?そしてめんどくさそう
ユーザープロフィール画面の作成
- データベースとスキーマの変更
users
テーブルの作成User
型とGuestbookEntry
型の更新
- バックエンドの実装
routes.ts
にユーザー関連のエンドポイントを追加- 仮ユーザーの作成
- フロントエンドの実装
GuestbookForm.tsx
の変更(名前入力欄の削除)GuestbookEntries.tsx
の変更(アバター画像の表示)
プランを立てるために結構考えている。都度ちゃんと何を考えているか、を伝えてくる。
2. User Experience Improvements
の User profiles with customizable avatars
から取り組んでいくようだ。またモリモリとファイルを作成していく
schema.ts 更新
「スキーマ変わる前にデータベース作成しないといけない」って言ってる。前回作成したものだとダメなのかな?わからん
DB 作り直しつつ users
テーブルを作成する
users
table を作成User
型の定義GuestbookEntry
型の定義を更新userId
を持つようにするUser
型のuser
も持つ
エンドポイントの追加
routes.ts にユーザー関連のエンドポイントを追加していく POST /api/users
とか
Now that we have our database schema set up, let’s update the routes.ts file to handle the new user-related endpoints and fix the existing entry creation to work with user IDs.
仮のユーザーを作成
まだ認証機能も実装していないんでとりあえず仮のユーザー作っておくで〜ってことで1人ユーザーを作成していた
INSERT INTO users (username, display_name)
VALUES ('default_user', 'Guest User')
RETURNING id;
id
1
INSERT 0 1
確かに1人ユーザーが作成されている。avatar_url
はまだない
フロントエンドコンポーネント
client/src/components/GuestbookForm.tsx
zod によるバリデーションから name
に対するバリデーションを取り除く。後々認証機能を実装する予定なので、投稿時に名前を入力するのはやめたため
エントリを載せるコンポーネントである client/src/components/GuestbookEntries.tsx
に投稿したユーザーのアバター画像を表示するようにしている
エントリをフェッチする際のエラーの修正
- エントリフェッチ時のエラー
- エラーの原因分析
- Replit Agentによる修正提案
- テーブルスキーマの修正
ALTER TABLE
とCREATE TABLE
の実行
この時点でエラーが出ているので修正していく
エントリをロードして表示することができていない
4:19:20 AM [express] GET /api/entries 500 in 293ms :: {"message":"Failed to fetch entries"}
これはおそらく、次のことが必要なためです:
- 最初に新しいusersテーブルを作成する
- user_id外部キーを追加するためにguestbook_entriesテーブルを修正する
- リアクションテーブルのスキーマを更新する
まあそうだろうなと。DBスキーマの更新は人間だったらもう少し事前に変更点を洗い出したりするものだが、修正のコストが安いのであれば、高速でトライ&エラーを進めるほうが早いのかもしれない
それでも適宜人間によるチェックは必要だろうけど
ALTER TABLE guestbook_entries
DROP COLUMN IF EXISTS name,
ADD COLUMN IF NOT EXISTS user_id INTEGER REFERENCES users(id),
ALTER COLUMN user_id SET NOT NULL;
-- Make sure reactions table is properly set up
CREATE TABLE IF NOT EXISTS reactions (
id SERIAL PRIMARY KEY,
entry_id INTEGER NOT NULL REFERENCES guestbook_entries(id),
emoji TEXT NOT NULL,
count INTEGER NOT NULL DEFAULT 1
);
NOTICE: relation "users" already exists, skipping
NOTICE: relation "reactions" already exists, skipping
ここまで3分くらい
ユーザープロフィールの表示と編集
UserProfile.tsx
の実装- プロフィール表示機能の作成
UserProfileEditor.tsx
の実装- プロフィール編集機能の作成
- ルーティングの設定
/profile
ルートの追加
- アバター画像のプレースホルダ表示
- アバターURLの空欄入力対応
Implement user avatars and display names in guestbook entries, remove name field from guestbook form and entry, and add user model.
アバター画像をまだ登録していない仮のユーザーだけど、エントリ部分で画像表示できるんだろか、と投稿してみると…
なんか GU
と表示されているぞ。なんだこれは
client/src/components/GuestbookEntries.tsx
return (
<div className="space-y-4">
{entries.map((entry) => (
<Card key={entry.id}>
<CardContent className="p-4">
<div className="flex items-start gap-4">
<Avatar className="h-10 w-10">
{entry.user.avatarUrl ? (
<AvatarImage src={entry.user.avatarUrl} alt={entry.user.displayName} />
) : (
<AvatarFallback>
{entry.user.displayName.slice(0, 2).toUpperCase()}
</AvatarFallback>
)}
</Avatar>
ここか。 avatarUrl
がない場合は displayName
の先頭2文字を大文字で表示するようになっている
出来上がってからはじめてコードをじっくり読む感じになるのか〜。でも人間相手のコードレビューでも同じことなのかもしれない
「エントリに画像表示されたか?メッセージ投稿できたか?ガンガン行くで〜」みたいなこと言ってる。やる気あるな
アバター画像付きのユーザープロフィールと編集可能なバイオセクションの追加
これは2分くらいでできた
client/src/pages/UserProfile.tsx
Let’s create a new UserProfile page component and a UserProfileEditor component to handle viewing and editing user profiles. We’ll need:
- A new route in App.tsx for the profile page
/profile
というルーティングも追加する- ユーザーが見つからなかった場合の
NotFound
コンポーネントも作成する
- A UserProfile page component
- A UserProfileEditor component with a form for editing profile details
- Navigation links to access the profile
export function UserProfile() {
const [isEditing, setIsEditing] = useState(false);
const { data: user, isLoading, error } = useQuery<User>({
queryKey: ["/api/users/1"], // Using user ID 1 for now
// 一旦仮で入れたユーザーID 1 で取得している
});
// (snip)
// プロフィール取得に失敗した時の処理。エラーメッセージと共に `Card` コンポーネントを表示する
// (snip)
return (
<div className="min-h-screen bg-background">
<div className="container max-w-2xl mx-auto px-4 py-8">
{isEditing ? (
<UserProfileEditor user={user} onCancel={() => setIsEditing(false)} />
) : (
<Card>
<CardContent className="p-6">
<div className="text-center">
<Avatar className="h-24 w-24 mx-auto mb-4">
{user.avatarUrl ? (
<AvatarImage src={user.avatarUrl} alt={user.displayName} />
) : (
<AvatarFallback>
{user.displayName.slice(0, 2).toUpperCase()}
</AvatarFallback>
)}
</Avatar>
<h1 className="text-2xl font-bold mb-1">{user.displayName}</h1>
<p className="text-muted-foreground mb-4">@{user.username}</p>
{user.bio && (
<p className="text-lg mb-4 whitespace-pre-wrap">{user.bio}</p>
)}
<div className="text-sm text-muted-foreground mb-4">
Joined {format(new Date(user.createdAt), "MMMM yyyy")}
</div>
<Button onClick={() => setIsEditing(true)}>Edit Profile</Button>
</div>
</CardContent>
</Card>
)}
</div>
</div>
);
// (snip)
client/src/components/UserProfileEditor.tsx
Let’s create a new UserProfile page component and a UserProfileEditor component to handle viewing and editing user profiles. We’ll need:
- A new route in App.tsx for the profile page
- A UserProfile page component
- A UserProfileEditor component with a form for editing profile details
- Navigation links to access the profile
UserProfile
作成時のリストと被っている部分はあるが、ここではプロフィール編集画面を作成する
まだホーム画面に導線を張っていなさそうだが、 /profile
にアクセスするとプロフィール画面が表示されるようになる
編集してみよう。んんんん、ゲストユーザーは画像がなかったのでてっきり今は無くてもいいのかな、と思ったがちゃんとバリデーションを効かせているのだな
とりあえずプレースホルダ用の画像 URL を登録しておく
https://placehold.co/100x100?text=Guest
バリデーションの調整
formSchema にて avatarUrl はオプショナルなのだが空欄で入力できないな?
質問してみたところ .or(z.literal(''))
で空文字列を許容するそうだ
client/src/components/UserProfileEditor.tsx
const formSchema = z.object({
displayName: z.string().min(1, "Display name is required").max(50),
bio: z.string().max(500).optional(),
avatarUrl: z.string().url("Must be a valid URL").optional().or(z.literal('')),
});
しかし空欄にしても保存に失敗してしまう。 routes.ts
も修正しないといけなかった
const schema = z.object({
displayName: z.string().min(1).max(50),
bio: z.string().optional(),
avatarUrl: z.string().url().optional().or(z.literal('')),
});
これで空欄で保存できるようになった
プロフィール詳細でも、画像が無い場合は名前のはじめの2文字が表示される
さらにテストを新規追加する
- UserProfile コンポーネント - プロフィールの表示と編集機能をテスト
- GuestbookForm コンポーネント - 既にテストファイルがあるが、新しいユーザーシステムに合わせて更新が必要
- GuestbookEntries コンポーネント - ユーザー情報を含むエントリのレンダリングをテスト
途中で訳がわからなくなったので詳細は省くが、どうもテストが通らない。config などを更新させたのだが、無理だった
仕方ないのでポチってテストケース追加前まで戻した
戻したはいいが npm run test
でエラーが出ているし、アプリ自体が立ち上がっていない。つまり変更前からテストが落ちていたのかもしれない
この辺りは npm run test
(vitest) で watch mode でずっと立ち上げておけばとりあえず気づくことはできそうだ
既存テストケースの修正
test/components/GuestbookForm.test.tsx
- Toaster コンポーネントを追加(トースト通知のために必要)
- テストケースの期待値を更新(名前フィールドは不要になった)
テストケースの警告に対応する
expect(screen.getByRole("button", { name: /sign/i })).toBeInTheDocument();
Property 'toBeInTheDocument' does not exist on type 'Assertion<HTMLElement>'.
https://qiita.com/Sicut_study/items/6feb6e7a9a7450eeb509
まあこの記事を見ればわかるのだが、AI に修正してもらう
CompilerOptions
に いくつか追加する
- includeに
test/**/*
を追加 - typesに
@testing-library/jest-dom
とvitest/globals
を追加 - excludeをシンプルに整理
tsconfig.jsonの変更:
- includeに
test/**/*
を追加: テストファイルをTypeScriptのコンパイル対象に含めるために必要でした - typesに
@testing-library/jest-dom
とvitest/globals
を追加: テストライブラリの型定義を認識させるために必要でした
これらの変更は、テストファイルで toBeInTheDocument()
のような@testing-library/jest-domのmatchers
を使用する際に、TypeScriptがそれらの型を認識できるようにするために必要でした。
感想
- うーむ、途中わけがわからなくなってしまったが、以前の checkpoint まで戻せるのは便利だ
- テストケースの作成が意外とうまくいかない
- watch mode で立ち上げておく
- checkpoint ごとにきちんとテストを走らせるように指示すると良いかもしれない