現在個人開発をしています。前回はDockerで動くようにしました。
今回は、フロントUI部分の実装を進めていきます。
一応デザインは作ったのですが、「デザイン苦手」「個人開発なので開発しながら調整をしていく」といった理由でガチガチにデザイン通りのものを作っていくという感じではなく、ゆるーく開発していければと思います。
下記のような作りになります。
- トップページでは簡単なアプリの紹介とログインボタン
- Googleログインのみ
- ログイン後はユーザー一覧画面に遷移
- 隊時から調べるページ / 任地から調べるページ / 現住所から調べるページ
- 絞り込んだ後の一覧ページ
- ユーザーページ
- 絞り込んだ後の一覧ページ
- ユーザー登録ページ
- その他静的ページ
ディレクトリ構成は次のようにしました。(変更する可能性大)
🗂️app
├ page.tsx
├ layout.tsx
├ 🗂️[profile]
│ └ 🗒️page.tsx
├ 🗂️currentaddress
│ ├ 🗒️page.tsx
│ └ 🗂️[currentaddress]
│ └ 🗒️page.tsx
├ 🗂️cohorts
│ ├ 🗒️page.tsx
│ └ 🗂️[year]
│ └ 🗒️page.tsx
├ 🗂️dispatchedcountry
│ ├ 🗒️page.tsx
│ └ 🗂️[region]
│ └ 🗒️page.tsx
├ 🗂️dashboard
│ └ 🗂️setting
│ └ 🗒️page.tsx
└ ...(その他の静的ぺージ)
各ページ
全てのページを説明すると長くなりそうなのでトップページと現住所から探すページのみに絞って説明します。
トップページ(/
)
ログイン前とログイン後で表示させる内容を変更したいですが、ログイン機能はまだ作っていないので2つ別で用意しておこうと思います。
👀 コードを確認する
import Footer from '@/components/Footer';
import Image from 'next/image';
import Button from '@/components/Button';
import Header from '@/components/Header';
export default function Home() {
return (
<>
<Header />
<main className="">
<div className="px-4">
<div className="w-16 mt-24 mx-auto">
<Image src="/images/logo.png" alt="logo" width={150} height={150} />
</div>
<h1 className="text-2xl text-center mt-6">協力隊の輪</h1>
<div className="text-center mt-10">
<Button>ログイン</Button>
<div className="mt-16">
<p>JOCVのつながりは、青年海外協力隊のOBOGの向けのサービスです。</p>
<p className="mt-6">
自分の「名前」「隊次」「任地」「現在住んでいる場所」「SNS」情報を登録してOBOGと繋がるきっかけの場所です。
</p>
</div>
</div>
</main>
<Footer />
</>
);
}
ヘッダー・フッター・ボタンは共通で利用するのでモジュールとして切り出しています。
現住所から調べるページ(/currentaddress/
)
エリア別に検索したいケースが出てきそうなので、エリア情報を持たせてみました。
都道府県の情報はconstants
ディレクトリに格納しておきます。
/constants/prefectures.ts
👀 コードを確認する
export type Prefecture = {
name: string;
romaji: string;
};
export type Region = {
name: string;
prefectures: Prefecture[];
};
export const JAPAN_REGIONS: Region[] = [
{
name: '北海道・東北',
prefectures: [
{ name: '北海道', romaji: 'hokkaido' },
{ name: '青森県', romaji: 'aomori' },
{ name: '岩手県', romaji: 'iwate' },
{ name: '宮城県', romaji: 'miyagi' },
{ name: '秋田県', romaji: 'akita' },
{ name: '山形県', romaji: 'yamagata' },
{ name: '福島県', romaji: 'fukushima' },
],
},
{
name: '関東',
prefectures: [
{ name: '茨城県', romaji: 'ibaraki' },
{ name: '栃木県', romaji: 'tochigi' },
{ name: '群馬県', romaji: 'gunma' },
{ name: '埼玉県', romaji: 'saitama' },
{ name: '千葉県', romaji: 'chiba' },
{ name: '東京都', romaji: 'tokyo' },
{ name: '神奈川県', romaji: 'kanagawa' },
],
},
{
name: '中部',
prefectures: [
{ name: '新潟県', romaji: 'niigata' },
{ name: '富山県', romaji: 'toyama' },
{ name: '石川県', romaji: 'ishikawa' },
{ name: '福井県', romaji: 'fukui' },
{ name: '山梨県', romaji: 'yamanashi' },
{ name: '長野県', romaji: 'nagano' },
{ name: '岐阜県', romaji: 'gifu' },
{ name: '静岡県', romaji: 'shizuoka' },
{ name: '愛知県', romaji: 'aichi' },
],
},
{
name: '近畿',
prefectures: [
{ name: '三重県', romaji: 'mie' },
{ name: '滋賀県', romaji: 'shiga' },
{ name: '京都府', romaji: 'kyoto' },
{ name: '大阪府', romaji: 'osaka' },
{ name: '兵庫県', romaji: 'hyogo' },
{ name: '奈良県', romaji: 'nara' },
{ name: '和歌山県', romaji: 'wakayama' },
],
},
{
name: '中国',
prefectures: [
{ name: '鳥取県', romaji: 'tottori' },
{ name: '島根県', romaji: 'shimane' },
{ name: '岡山県', romaji: 'okayama' },
{ name: '広島県', romaji: 'hiroshima' },
{ name: '山口県', romaji: 'yamaguchi' },
],
},
{
name: '四国',
prefectures: [
{ name: '徳島県', romaji: 'tokushima' },
{ name: '香川県', romaji: 'kagawa' },
{ name: '愛媛県', romaji: 'ehime' },
{ name: '高知県', romaji: 'kochi' },
],
},
{
name: '九州・沖縄',
prefectures: [
{ name: '福岡県', romaji: 'fukuoka' },
{ name: '佐賀県', romaji: 'saga' },
{ name: '長崎県', romaji: 'nagasaki' },
{ name: '熊本県', romaji: 'kumamoto' },
{ name: '大分県', romaji: 'oita' },
{ name: '宮崎県', romaji: 'miyazaki' },
{ name: '鹿児島県', romaji: 'kagoshima' },
{ name: '沖縄県', romaji: 'okinawa' },
],
},
];
都道府県のデータをインポートしてフロント部分で表示させます。
👀 コードを確認する
import Footer from '@/components/Footer';
import Header from '@/components/Header';
import SectionTitle from '@/components/SectionTitle';
import Link from 'next/link';
import { JAPAN_REGIONS } from '@/constants/prefectures';
export default async function CurrentCountry() {
return (
<>
<Header />
<main className="mt-8 mx-4">
<SectionTitle title="現住所" />
<div className="mt-8">
<div className="">
<h3 className="text-2xl">日本国内</h3>
{JAPAN_REGIONS.map((region) => (
<div key={region.name} className="mt-10">
<h4 className="text-xl">{region.name}</h4>
<ul className="grid grid-cols-2 gap-4 mt-4">
{region.prefectures.map((prefecture) => (
<li key={prefecture.name}>
<Link href={`currentaddress/${prefecture.romaji}`}>{prefecture.name}</Link>
</li>
))}
</ul>
</div>
))}
</div>
</div>
</main>
<Footer />
</>
);
}
ポイントは下記の部分かなと思います。
{JAPAN_REGIONS.map((region: Region) => (
<div key={region.name} className="mt-10">
<h4 className="text-xl">{region.name}</h4>
<ul className="grid grid-cols-2 gap-4 mt-4">
{region.prefectures.map((prefecture: Prefecture) => (
<li key={prefecture.name}>
<Link href={`currentaddress/${prefecture.romaji}`}>{prefecture.name}</Link>
</li>
))}
</ul>
</div>
))}
JAPAN_REGIONS
の配列の各要素(地域)に対して処理を行います。
型を定義しておくことで、regin/prefectureの型がわかりやすくていいですね。
(業務でPHPを使っていて毎回どんなデータが入っているのかを確認しないといけないつらみを感じているので大変ありがたい)
まとめ
他のコードはこちらのPRにまとまっているので興味があれば確認ください👏🏻